字符集转换不会直接导致死锁,但会因隐式转换使索引失效,扩大锁范围至间隙锁或临键锁,多事务大范围加锁且顺序不一致时易形成循环等待而引发死锁。

字符集转换不会直接导致死锁,但会间接引发死锁——根源在隐式类型/字符集转换破坏了索引使用,进而扩大锁范围。
为什么 COLLATE 或字符集不匹配会让死锁更频繁
当查询字段和参数的字符集或排序规则(COLLATE)不一致时,MySQL 无法使用索引进行等值查找,被迫走全表扫描或范围扫描。这会导致:
- 行锁升级为间隙锁(
gap lock)甚至临键锁(next-key lock),锁住本不该锁的索引间隙 - 原本只锁 1 行的
UPDATE user SET name='a' WHERE phone='138...',变成锁住整个phone索引段 - 多个事务在相同大范围内加锁 + 加锁顺序不一致 → 极易形成循环等待
WHERE 条件中隐式 CONVERT() 是死锁温床
执行 EXPLAIN 后若看到 type: ALL 或 Extra: Using where; Using index condition 但实际没走索引,大概率存在隐式转换。常见诱因:
- 字段定义为
VARCHAR(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_cs,而传入参数是utf8mb4_general_ci - 应用层用 JDBC 传参时未指定
useUnicode=true&characterEncoding=utf8mb4,导致连接默认用latin1 - 建表时混合使用
utf8(即utf8mb3)和utf8mb4,触发强制转换
此时 MySQL 内部会插入 CONVERT(phone USING utf8mb4),使索引失效 —— 锁范围失控,死锁概率陡增。
如何一步到位规范化索引字段的字符集与 COLLATE
目标:确保查询条件能命中索引,且锁粒度严格控制在单行或明确范围。操作分三步:
- 查清字段真实定义:
SHOW FULL COLUMNS FROM users LIKE 'phone';关注Collation列 - 统一连接层编码:在数据库连接字符串中显式指定
characterEncoding=utf8mb4&collationServer=utf8mb4_0900_as_cs - 重建字段(如需):
ALTER TABLE users MODIFY phone VARCHAR(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_cs;(注意:该操作会锁表,建议低峰期执行)
关键点:避免用 utf8mb4_general_ci —— 它不区分大小写且对某些符号比较宽松,容易让 MySQL 在比较时放弃索引;优先选 utf8mb4_0900_as_cs(大小写敏感、重音敏感),它支持索引下推(ICP)且转换行为可预测。
验证是否真正修复:从 INFORMATION_SCHEMA 和死锁日志交叉比对
仅靠 EXPLAIN 不够,必须确认运行时是否真用了索引、锁了哪些记录:
- 开启死锁记录:
SET GLOBAL innodb_print_all_deadlocks = ON;,日志中若不再出现“waiting forlock_mode X locks gap before rec insert intention”,说明间隙锁大幅减少 - 查锁状态:
SELECT * FROM performance_schema.data_locks WHERE OBJECT_NAME = 'users';,对比修复前后LOCK_DATA的具体值(应从多个区间收敛为单个主键值) - 检查索引有效性:
SELECT INDEX_NAME, SEQ_IN_INDEX, COLUMN_NAME FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_NAME = 'users' AND COLUMN_NAME = 'phone';,确保SEQ_IN_INDEX = 1且无其他干扰列
最隐蔽的坑是:字段改了 COLLATE,但对应索引没重建 —— ALTER TABLE ... MODIFY 不自动重建索引,必须手动 DROP INDEX 再 ADD INDEX 才生效。


















