mysql触发器中不能直接用like匹配敏感词,因不支持子查询且函数需声明reads sql data;postgresql可用正则+数组实现;高并发下应优先应用层缓存过滤。

触发器里不能直接用 LIKE 做模糊敏感词匹配
因为大多数数据库的触发器(尤其是 MySQL 5.7/8.0 的 BEFORE INSERT)不支持在触发器体中执行子查询去查敏感词表,更无法用 LIKE '%xxx%' 遍历所有词——会报错 This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA。真正能跑通的方式是把敏感词预加载进内存结构,或改用函数封装逻辑。
MySQL 中必须用存储函数 + 触发器组合实现
先创建一个返回是否含敏感词的函数,再在触发器里调用它。注意:该函数必须显式声明为 READS SQL DATA,否则触发器调用时仍会失败。
- 创建敏感词表:
CREATE TABLE sensitive_words (word VARCHAR(100) NOT NULL) - 定义函数:
DELIMITER $$ CREATE FUNCTION contains_sensitive(text TEXT) RETURNS BOOLEAN READS SQL DATA DETERMINISTIC BEGIN DECLARE done INT DEFAULT FALSE; DECLARE w VARCHAR(100); DECLARE cur CURSOR FOR SELECT word FROM sensitive_words; DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; <p>OPEN cur; read_loop: LOOP FETCH cur INTO w; IF done THEN LEAVE read_loop; END IF; IF LOCATE(w, text) > 0 THEN RETURN TRUE; END IF; END LOOP; CLOSE cur; RETURN FALSE; END$$ DELIMITER ;</p>
- 在触发器中使用:
IF contains_sensitive(NEW.content) THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Content contains banned words'; END IF;
PostgreSQL 可直接用正则 + ARRAY 轻松过滤
PostgreSQL 触发器支持在 PL/pgSQL 中查表、构造数组、用 ~* 做不区分大小写的正则匹配,写法更直观且无需额外函数。
- 假设敏感词存在
sensitive_words(word TEXT)表中 - 触发器内可这样写:
DECLARE bad_words TEXT[]; BEGIN SELECT ARRAY_AGG(word) INTO bad_words FROM sensitive_words; IF NEW.content ~* array_to_string(bad_words, '|') THEN RAISE EXCEPTION 'Content contains banned words'; END IF; RETURN NEW; END; - 注意:
array_to_string生成的正则表达式需确保每个词都加字边界(\y)才精准,否则“枪”会误杀“手枪”;实际应改用:ARRAY_TO_STRING(ARRAY(SELECT '\y' || word || '\y' FROM sensitive_words), '|')
性能和维护成本比想象中高得多
每次 INSERT 都要全表扫描敏感词表 + 逐个字符串匹配,哪怕只有 200 个词,在高并发留言场景下也会明显拖慢写入速度。更现实的做法是:把敏感词加载到应用层缓存(如 Redis 的 SET 或 SCARD),由后端服务做前置过滤;数据库只承担最终兜底校验,且仅检查高频短词(如单字、两字词),长句交给 NLP 模型或专用审核服务。











