面试题答案
一键面试定位死锁产生原因的步骤
- 查看日志:SQLite 本身日志可能有限,但应用层日志若记录了 SQL 执行顺序、时间等,可从中分析事务执行序列。例如,若日志记录事务 A 先执行
UPDATE table1 SET column1 = 'value1' WHERE id = 1
,紧接着事务 B 执行UPDATE table2 SET column2 = 'value2' WHERE id = 2
,后续又有冲突操作,可能与这顺序有关。 - 分析事务操作顺序:通过监控工具或在应用代码中添加调试输出,确定多个并发事务对数据库对象(如表、行)的访问顺序。比如事务 T1 先锁定表 A 的一行,再尝试锁定表 B 的一行;而事务 T2 先锁定表 B 的一行,再尝试锁定表 A 的一行,就可能导致死锁。
- 检查锁定模式:确认事务对数据的锁定模式(共享锁、排他锁等)。若事务 A 以排他锁锁定数据行 X,事务 B 也试图以排他锁锁定 X,就可能造成死锁等待。
从数据库设计、事务管理方面预防死锁
数据库设计
- 合理索引设计:创建适当索引可减少锁的粒度和持有时间。例如,在经常作为
WHERE
条件的列上创建索引,使查询能更精准定位数据,避免全表扫描导致的大量锁。如SELECT * FROM users WHERE username = 'john'
,若username
列有索引,查询可快速定位行,减少锁范围。 - 优化表结构:避免不必要的复杂连接和嵌套表结构。例如,将大表按功能或使用频率拆分为多个小表,降低单个事务涉及的锁资源。若有一个包含用户基本信息、订单信息等多种数据的大表,可拆分为
users
表和orders
表,不同事务可分别操作不同表,减少锁冲突。
事务管理
- 按相同顺序访问资源:确保所有事务以相同顺序访问数据库资源。比如所有事务都先访问表 A,再访问表 B。若事务 T1 总是先执行
UPDATE tableA SET columnA = 'valueA' WHERE id = 1
,然后执行UPDATE tableB SET columnB = 'valueB' WHERE id = 2
;事务 T2 也按此顺序操作,就能避免因访问顺序不同导致的死锁。 - 减少事务持有锁的时间:事务中尽量减少不必要的操作,尽早释放锁。例如,将事务内可拆分的操作拆分,先提交一部分事务。假设一个事务既要更新用户信息,又要更新订单信息,可先更新用户信息并提交,再更新订单信息,这样可缩短锁的持有时间。
- 设置合理的事务隔离级别:根据业务需求选择合适隔离级别。例如,若业务对一致性要求不高,可选择较低隔离级别(如读未提交),减少锁的使用;但要注意可能出现脏读等问题。而对于一致性要求高的业务,选择可串行化隔离级别时,要考虑性能影响,适当优化事务逻辑以减少锁等待。