2026/2/17 19:09:07
网站建设
项目流程
厦门亚龙网站建设,怎么给网站做手机端,网站运营与推广计划书,徐汇网站建设推广死锁是数据库里很常见的问题#xff1a;两个或多个事务互相等待对方释放锁#xff0c;结果谁也动不了。MySQL的InnoDB引擎会自己自动检测死锁#xff0c;并且回滚其中一个事务来解决#xff0c;但这种情况如果经常遇到的话#xff0c;会很影响性能和用户体验。其实#x…死锁是数据库里很常见的问题两个或多个事务互相等待对方释放锁结果谁也动不了。MySQL的InnoDB引擎会自己自动检测死锁并且回滚其中一个事务来解决但这种情况如果经常遇到的话会很影响性能和用户体验。其实只要注意一些设计细节就能大大减少甚至避免死锁。下面是几个最实用的方法1. 事务要短动作要快事务越长锁住数据的时间就越久别人就越容易“撞上”你。正确做法只在事务里做必要的数据库操作别把业务逻辑比如调接口、算数据塞进去。sql-- 不推荐事务中混杂业务逻辑 START TRANSACTION; SELECT * FROM users WHERE id 1; -- 假设此处有耗时的业务处理... UPDATE accounts SET balance balance - 100 WHERE user_id 1; UPDATE orders SET status paid WHERE user_id 1; COMMIT; -- 推荐事务只包含必要数据库操作 START TRANSACTION; UPDATE accounts SET balance balance - 100 WHERE user_id 1; UPDATE orders SET status paid WHERE user_id 1; COMMIT;2. 所有事务按同一个顺序操作表这是避免死锁最有效的一招比如如果多个事务都要改 users 和 orders 表那就统一先改 users再改 orders。不要有的先改 users有的先改 orders。sql-- 所有地方都这样写 UPDATE users SET ... WHERE id 1; UPDATE orders SET ... WHERE user_id 1;只要顺序一致就不会出现“A等B、B等A”的循环等待。3. 给表加合适的索引InnoDB 的行锁是靠索引来实现的。如果查询没用到索引MySQL 就可能锁住整张表或很多无关的行大大增加死锁风险。建议经常用来查或更新的字段比如 user_id要建索引。用 EXPLAIN 看看 SQL 是否命中索引。sqlCREATE INDEX idx_user_id ON orders(user_id);4. 别用太高的隔离级别除非必要MySQL 默认是 REPEATABLE READ它会加“间隙锁”防止幻读但也更容易死锁。如果你的业务能接受“读已提交”比如允许看到别人刚提交的数据可以改成sqlSET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;这样锁的范围更小死锁概率更低。5. 显式加锁时要小心如果你要用 SELECT ... FOR UPDATE 锁行一定要确保条件能命中索引锁的行尽量少事务尽快结束。sql-- 安全通过主键或索引锁定一行 SELECT * FROM accounts WHERE user_id 1 FOR UPDATE;如果 user_id 没索引这条语句可能锁住成千上万行6. 应用层要有重试机制死锁偶尔还是会发生。这时候应用应该捕获死锁错误MySQL 错误码 1213 或 SQLSTATE 40001自动重试几次比如最多 2~3 次每次重试前等一小会儿比如 100ms、200ms…。java// 伪代码示例 for (int i 0; i 3; i) { try { doDatabaseUpdate(); break; // 成功就退出 } catch (DeadlockException e) { sleep(100 * (i 1)); // 等一下再试 } }7. 大批量更新要分批做一次更新几万行这很容易锁住大量数据引发死锁或卡顿。正确做法每次只改 500~1000 行改完提交再继续。sql-- 分批更新 UPDATE large_table SET status done WHERE create_time 2023-01-01 AND status ! done LIMIT 1000; -- 循环执行直到没有数据可更新8. 避免热点数据被频繁修改比如一个全局计数器所有请求都去 UPDATE counter SET value value 1那这一行就成了堵点。解决办法用分桶计数。sql-- 把计数分散到 10 个桶里 UPDATE counter_buckets SET value value 1 WHERE name views AND bucket FLOOR(RAND() * 10); -- 查总数时再加起来 SELECT SUM(value) FROM counter_buckets WHERE name views;9. 出问题了怎么查看最近一次死锁详情sqlSHOW ENGINE INNODB STATUS;找LATEST DETECTED DEADLOCK部分。查当前正在运行的事务MySQL 8.0sqlSELECT * FROM performance_schema.data_locks; SELECT * FROM information_schema.INNODB_TRX;总结1.事务要短别拖着不提交。 2.顺序要一致所有人按相同顺序改表。 3.索引要到位避免锁太多无关数据。 4.出错要重试应用层兜底处理死锁。 5.大批量要分批别一次锁太多行。死锁没法完全杜绝但只要做好这些基本就不会再被它困扰了