存储过程本身不自动保证一致性,必须由调用方显式开启事务(BEGIN)、调用存储过程、再根据结果决定COMMIT或ROLLBACK;过程内仅负责业务逻辑与异常捕获,且所有表须为InnoDB引擎,配合约束、校验及正确异常处理(如SIGNAL)方可确保数据一致。

存储过程本身不自动保证一致性,必须配合显式事务、约束和校验逻辑才能生效;单独写个 CALL check_consistency() 而不控制事务边界,等于没做。
事务必须由调用方启动,不是存储过程自己BEGIN
很多人在存储过程里写 START TRANSACTION,以为这样就能回滚,结果发现 ROLLBACK 无效。根本原因是:MySQL 默认 autocommit=1,每条语句执行完立刻提交,存储过程内的 START TRANSACTION 只对当前过程内语句有效,一旦过程退出,事务就隐式提交了。
- 正确做法是:应用层或上层 SQL 显式执行
BEGIN→CALL proc_xxx()→COMMIT或ROLLBACK - 存储过程内部只负责业务逻辑和异常捕获,不能替代事务起点
- 如果过程里执行了
ALTER TABLE或CREATE INDEX,会触发隐式COMMIT,前面所有 DML 都无法回滚 - 跨表操作时,所有表必须是 InnoDB 引擎;混用 MyISAM 会让整个事务失去原子性
用 DECLARE EXIT HANDLER 捕获异常但别指望它兜底
DECLARE EXIT HANDLER FOR SQLEXCEPTION 是刹车片,不是引擎——它只能在已开启的事务里起作用。没事务,ROLLBACK 就无事可滚。
- handler 必须搭配显式事务使用,且应记录日志:
INSERT INTO error_log (msg, proc_name) VALUES (ERROR_MESSAGE(), 'proc_transfer') - 避免在 handler 里再执行可能失败的操作(比如写日志表时磁盘满),否则会掩盖原始错误
- MySQL 不支持
RESIGNAL向上抛错,如需通知调用方失败,建议用SIGNAL SQLSTATE '45000'主动中断 - handler 对
WARNING不响应,只有EXCEPTION级别错误才会触发
结构比对要查系统表,不能只拼DDL字符串
用 mysqldump --no-data 导出再 diff,容易因空格、注释、列顺序不同误报;真正影响一致性的差异藏在元数据里。
- 关键字段必须逐项比对:
information_schema.COLUMNS中的data_type、is_nullable、column_default(注意带括号的函数默认值如(NOW()))、collation_name - 字符集和排序规则不一致(如
utf8mb4_0900_ai_civsutf8mb4_general_ci)会导致 JOIN 或 ORDER BY 结果不同 - 新增字段若带
NOT NULL且无默认值,MySQL 会拒绝执行ALTER TABLE,必须先加默认值再改约束 - 禁止在存储过程中用
PREPARE/EXECUTE动态执行ALTER,MySQL 8.0 下部分语法会报ER_UNSUPPORTED_ALTER_IN_PREPARE
触发器里不能 SELECT 触发表,OLD/NEW 是唯一安全来源
想在 BEFORE UPDATE 里查旧值?写 SELECT old_col INTO @v FROM t WHERE id = OLD.id 直接报错 ERROR 1442。这是 MySQL 的硬性限制,不是 bug。
- 唯一合法方式是直接用
OLD.col和NEW.col做比较,比如IF OLD.status != NEW.status THEN ... - 需要关联其他表校验时,必须把逻辑移到应用层或异步任务,不能塞进触发器
- INSERT/UPDATE/DELETE 必须分开建触发器,MySQL 不支持单触发器监听多事件
- 抛错必须用
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'status transition not allowed',RETURN或SELECT不会中断主语句
一致性不是靠某一个功能实现的,而是事务边界、约束定义、元数据比对、触发器行为四者严丝合缝的结果;漏掉任意一环,检查就形同虚设。

















