MySQL保存/修改/删除触发器时导致整个程序冻结问题
问题成因
你遇到的MySQL冻结问题是触发器代码存在死循环导致的,具体问题点如下:
- 游标循环无终止逻辑:当游标遍历完
ps_ets_ept_alegenos表所有数据后,会触发NOT FOUND处理器将ch_done设为1,但你的代码没有在检测到ch_done=1时执行LEAVE语句跳出循环,后续的字符串匹配、拼接、变量重置逻辑会无限重复执行,持续消耗数据库连接资源,最终导致程序无响应。 - 状态判断逻辑冗余错误:你在检测到
ch_done=1时仅重复给ch_done赋值为1,没有做任何拦截处理,游标读到末尾后会反复触发无效fetch,永远走不到关闭游标、结束触发器的逻辑。 - 额外性能隐患:该触发器绑定UPDATE操作后,每次更新任意产品都会全表扫描过敏原表做字符串匹配,还会往
pruebaTablas表插入数据,更新频繁时很容易引发表锁、行锁等待,进一步加剧卡死问题。
修复后可正常运行的触发器代码
BEGIN declare contentCursor text; declare tableContent text default '<table width="250" cellpadding="0" cellspacing="0"><tbody>'; declare alegenos varchar(30); declare contieneAlergenos int; declare trazasCerca int; declare siCerca text default ""; DECLARE ch_done INT DEFAULT 0; DECLARE alegenosCursor cursor for select Nombre from ps_ets_ept_alegenos; DECLARE CONTINUE HANDLER FOR NOT FOUND SET ch_done = 1; BLOCK2: begin open alegenosCursor; bucleAlergenos:LOOP fetch alegenosCursor into alegenos; -- 读到游标末尾立刻跳出循环,避免死循环 IF ch_done = 1 THEN LEAVE bucleAlergenos; END IF; -- 匹配当前过敏原是否在旧内容中存在 SELECT LOCATE(alegenos, old.content) into contieneAlergenos; if contieneAlergenos>0 then -- 判断是痕量(Trazas)还是明确含有(Si) SELECT LOCATE("Trazas",SUBSTR(old.content,contieneAlergenos+length(alegenos)+44,7)) INTO trazasCerca; if trazasCerca>0 then SET siCerca="Trazas"; else SET siCerca="Si"; end if; -- 拼接含有/痕量的表格行 SELECT CONCAT(tableContent,'<tr><td width="60%" align="left"><strong>', alegenos, '</strong></td><td width="40%" align="left"> ', siCerca, '</td></tr>') INTO tableContent; else -- 拼接不含该过敏原的表格行 SELECT CONCAT(tableContent,'<tr><td width="60%" align="left"><strong>', alegenos, '</strong></td><td width="40%" align="left">No</td></tr>') INTO tableContent ; end if; -- 重置临时变量 SET siCerca=""; SET trazasCerca=0; SET contieneAlergenos=0; END LOOP bucleAlergenos; -- 拼接表格闭合标签,写入测试表 select CONCAT(tableContent,'</tbody></table>') INTO tableContent; insert into pruebaTablas VALUES (tableContent); set tableContent=""; close alegenosCursor; END BLOCK2; END
更优实现思路
- 不建议在数据库触发器中做HTML格式拼接:数据库的核心职责是存储、查询结构化数据,HTML渲染属于前端/业务层逻辑,放到业务代码中实现后续调整样式、修改规则不需要变更数据库逻辑,也不会给数据库增加额外的计算压力。
- 避免用游标逐行处理字符串拼接:如果确实需要在数据库层生成拼接结果,可以用
GROUP_CONCAT函数一次性聚合所有过敏原的匹配结果,不需要手动写游标循环,性能会有明显提升。 - 替换字符串匹配的过敏原判断逻辑:不要用
LOCATE做字符串模糊匹配判断产品是否含有过敏原,建议单独建产品-过敏原关联表,结构化存储每个产品对应过敏原的状态(无/含有/痕量),从根源上避免字符串匹配的误差,查询和维护效率更高。
内容的提问来源于stack exchange,提问作者Kimuy
相关产品推荐
相关产品推荐

