百度面试题:数据库出现死锁,该如何排查解决?

科技   2024-12-21 14:01   陕西  

今天咱们来聊聊数据库里一个让开发者们挠头的经典问题——死锁。先别慌,死锁其实并不可怕,可怕的是你不知道它是怎么来的,又该怎么去解决。

死锁这东西,说白了就是多个事务彼此死盯着对方手里的资源,谁也不松手,搞得大家都干不下去了。就像两辆车在单行道上对头相遇,谁都不愿意倒车,只能干瞪眼。那数据库里的死锁是怎么发生的呢?

死锁的常见触发原因

1. 资源竞争导致死锁
资源是有限的,但需求无限啊!比如多个事务试图同时锁定同一行数据,获取资源的顺序又不一致,就容易互相等待。这种情况特别容易在并发场景下发生。

举个简单的例子:

-- 事务1
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
-- 试图更新另一行
UPDATE accounts SET balance = balance + 100 WHERE account_id = 2;

-- 事务2
BEGIN;
UPDATE accounts SET balance = balance + 100 WHERE account_id = 2;
-- 试图更新第一行
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;

事务1和事务2各自占着一个资源,非要对方松手,最终谁也执行不了,这就形成了死锁。

2. 未及时释放资源
有时候死锁的原因很“低级”,比如事务获取了资源却迟迟不提交或回滚,导致其他事务干等着。写代码的时候一个不小心忘了加COMMIT或者ROLLBACK,就是事故的根源。

3. 不同事务执行速度差异
如果某些事务执行得特别慢,持有资源的时间过长,其他事务没法拿到锁,这种情况下死锁也经常发生。

4. 复杂查询带来的隐形死锁
有些时候我们以为只操作了一条记录,其实由于索引的作用,数据库内部加了多个锁,结果产生了锁冲突。例如:

UPDATE my_table SET age = 22 WHERE name = 'John';

如果name字段有索引,这条语句会先锁住索引,再锁住主键,这时另一个事务按照不同的顺序加锁就会触发死锁。

如何排查和解决死锁问题

排查死锁就像破案,得搞清楚是哪些事务在互相“掐架”,还要找到“起因”。

1. 使用数据库的死锁日志以MySQL为例,死锁发生时,InnoDB存储引擎会记录相关信息,使用以下命令查看:

SHOW ENGINE INNODB STATUS;

在日志中你会看到死锁涉及的事务、加锁的表、锁类型等信息。通过这些内容,基本能定位到问题的核心。

2. 优化事务逻辑

固定资源访问顺序

无论哪个事务,都按照相同的顺序访问资源。例如:

-- 规定只能先更新 account_id=1,再更新 account_id=2
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
UPDATE accounts SET balance = balance + 100 WHERE account_id = 2;

减少事务的执行时间

把事务操作控制在最小的时间范围内,能commit的尽量早commit,不要拖泥带水。

3. 降低锁的粒度

锁的粒度越小,发生冲突的概率就越低。比如尽量避免表级锁,优先使用行级锁:

-- 大范围锁
SELECT * FROM orders FOR UPDATE;

-- 优化后
SELECT * FROM orders WHERE order_id = 123 FOR UPDATE;

4. 降低隔离级别

如果业务允许,可以适当降低隔离级别,比如从REPEATABLE READ调整为READ COMMITTED。这会减少长时间持有锁的场景。

实战案例:只操作一条记录也会死锁?

是的,你没看错!即使只操作一条记录,也可能因为锁的种类和顺序问题导致死锁。

看下面这个例子:

-- 事务1
UPDATE my_table SET age = 25 WHERE id = 1;

-- 事务2
SELECT * FROM my_table WHERE id = 1 FOR UPDATE;

事务1先对记录加了行锁,然后试图获取主键锁;事务2先获取主键锁,再获取行锁。顺序不一致,互相等待,死锁就发生了。

解决办法是确保锁的顺序统一,比如明确规定所有操作必须先获取主键锁,再获取其他锁。

如果你在面试中被问到“如何预防和解决数据库死锁”,可以这样回答:

1. 预防措施:

  • 保持一致的资源访问顺序,避免不同事务因访问顺序不同导致死锁。
  • 减少锁的粒度,比如使用行级锁代替表级锁。
  • 优化事务逻辑,缩短事务执行时间,减少资源占用时长。
  • 选择合适的隔离级别,在允许的范围内降低隔离级别,比如从REPEATABLE READ调整为READ COMMITTED

2. 排查与解决:

  • 使用SHOW ENGINE INNODB STATUS查看死锁日志,定位冲突事务和锁类型。
  • 根据死锁日志调整SQL语句和事务逻辑,避免不必要的锁竞争。
  • 如果无法避免死锁,可以考虑增加重试机制。多数数据库支持事务回滚后自动重试。

总之,数据库死锁这个问题说难也不难,只要你清楚它是怎么来的,遵循一些基本的编程规范,就能大大减少发生的可能性。如果真的不幸遇到,也别慌,靠日志分析和优化事务逻辑,妥妥能搞定!

对编程、职场感兴趣的同学,大家可以联系我微信:golang404,拉你进入“程序员交流群”。
🔥虎哥私藏精品 热门推荐🔥

虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》

资料包含了《IDEA视频教程》《最全python面试题库》《最全项目实战源码及视频》《毕业设计系统源码》,总量高达650GB全部免费领取

Python技术迷
回复:python,领取Python面试题。分享AI编程,AI工具,Python技术栈,Python教程,Python编程视频,Pycharm项目,Python爬虫,Python数据分析,Python核心技术,Python量化交易。
 最新文章