备案站点的数据库死锁排查与预防
作者:备案源头内容团队内容审核:备案源头内容团队更新于 2026年9月26日
两个事务互相等对方先放手,谁都等不到
死锁的经典场景是这样的:事务 A 先锁住了记录 1,正要去锁记录 2;同一时刻事务 B 先锁住了记录 2,正要去锁记录 1。两边都在等对方释放自己需要的锁,而对方偏偏也在等着自己——这个循环等待谁也解不开,数据库唯一的出路是主动介入,选择牺牲其中一个事务(直接回滚),让另一个继续往下走。死锁不是"数据库出故障了",而是数据库设计里必然存在的一种正常处理机制,运维要做的不是消灭死锁的可能性(做不到),而是让死锁发生的频率降到可接受范围,并且让应用能正确应对被牺牲的那一方。
关键要点
- 死锁和普通的锁等待是两件事:锁等待会随着持锁事务提交而自然解开,死锁是循环等待,永远不会自然解开,必须有一方被强制回滚。
- 加锁顺序不一致是死锁最常见的根因:不同事务对同一批资源以不同顺序加锁,迟早会撞上循环等待。
- 数据库的死锁检测机制会自动选择牺牲者并报错,但应用层如果没有对应的重试逻辑,用户看到的就是一次莫名其妙的失败。
- 事务隔离级别越严格,加锁范围往往越大,死锁概率也随之上升,这是一致性和并发能力之间的经典取舍。
死锁和普通锁等待:表现相似,本质不同
| 现象 | 锁等待 | 死锁 |
|---|---|---|
| 等待对象 | 等待一个会主动释放锁的事务 | 等待一个同样在等自己的事务 |
| 能否自行解开 | 能,等对方提交或回滚后自然获得锁 | 不能,双方陷入循环等待 |
| 数据库的处理方式 | 排队等待,最多触发锁等待超时 | 主动检测并强制回滚其中一方 |
| 应用侧的感知 | 请求变慢 | 请求直接报错(被回滚) |
排查时第一步要分清楚是哪一种:如果只是等待时间变长,问题通常在于某个事务持锁时间过长(比如事务里夹了远程调用),处理思路和长事务治理是同一套,见 数据库连接池与连接数治理 中关于事务范围收敛的讨论;如果数据库明确报出死锁错误,那就是循环等待被自动解开了,需要去找加锁顺序上的问题。
加锁顺序不一致:死锁最常见的根因
死锁几乎总是源于同一个模式:不同的代码路径以不同的顺序访问同一批资源。举一个典型例子:一处代码"先扣减库存,再扣减余额",另一处代码"先扣减余额,再扣减库存",两处代码看起来都合理,各自单独跑也完全没问题,但当它们并发执行、又恰好命中同一批记录时,就构成了经典的循环等待条件。
预防的核心原则是让所有访问同一批资源的代码路径遵循同一个加锁顺序:
- 约定固定的资源访问顺序,比如永远按主键从小到大的顺序加锁,不同业务逻辑路径都遵循这个约定,而不是各自按业务上"顺手"的顺序来写。
- 减少一个事务里涉及的资源种类和数量,事务涉及的表和行越多,加锁顺序出现不一致的概率越高,事务边界的收敛思路见 高并发库存扣减与超卖防护 中关于原子操作范围的讨论。
- 批量操作时对涉及的记录预先排序,比如批量更新一组订单前先按订单号排序再逐条处理,避免不同批次任务因为处理顺序随机而互相形成循环等待。
死锁日志:怎么读出根因
数据库在检测到死锁并解开之后,通常会留下一份死锁日志,记录了参与死锁的各个事务分别持有什么锁、在等待什么锁。这份日志是排查死锁根因最直接的证据,但很多团队因为看不懂格式而直接跳过,只是简单加个重试就算了事——这样做的后果是同类死锁会反复出现,只是被重试悄悄掩盖了。
读日志时重点关注三件事:
- 参与死锁的是哪几条 SQL:对照实际业务代码,找出这几条 SQL 分别来自哪个业务操作。
- 各自已经持有的锁和正在等待的锁:这决定了循环等待的具体链路,通常能直接指向加锁顺序不一致的具体位置。
- 死锁发生的频率和时间分布:如果集中在某个业务高峰时段,说明是并发量放大了本就存在的加锁顺序问题;如果零星分散,可能是某个低频但确实存在缺陷的代码路径。
把死锁日志的采集和分析纳入常规的可观测性体系,而不是等用户反馈报错才去翻,日志的采集与检索方式见 日志规范与集中检索,死锁频率本身也应该是一项被监控的指标,见 备案网站监控告警体系。
隔离级别对锁范围的影响
事务隔离级别不是只影响"能不能看到别人未提交的数据"这么简单,它同时决定了加锁的范围和粒度,隔离级别越严格,通常意味着更大的锁定范围,死锁概率也随之上升:
| 隔离级别方向 | 并发能力 | 死锁概率 |
|---|---|---|
| 较宽松 | 较高,锁范围小 | 较低 |
| 较严格 | 较低,锁范围大(可能锁住范围而非仅锁住已存在的行) | 较高 |
这是一致性和并发能力之间的经典取舍,没有绝对正确的选择,只有适合具体业务场景的选择。核心原则是不要为了图省事而全局使用最严格的隔离级别,应该按业务场景的实际一致性需求来选择,对一致性要求不高的查询场景,适当放宽隔离级别往往能显著降低死锁概率,同时提升并发表现。
重试逻辑:两个必须满足的前提
数据库检测到死锁后,会主动回滚其中一个事务,这意味着被回滚的那一方,从应用的视角看就是一次事务执行失败,如果没有配合重试逻辑,用户会看到一次无法解释的操作失败,即使这个操作本身完全合理。给死锁配上重试是标准做法,但重试要满足两个前提:
- 重试的操作必须是幂等的,否则死锁触发的重试会导致重复执行,做法和其他场景下的幂等设计是同一套原则,见 超时设置与重试策略。
- 重试要有次数上限和退避策略,如果加锁顺序问题本身没有解决,无限重试只会让同样的死锁反复触发,见 超时设置与重试策略 中关于退避与重试预算的讨论同样适用于这里。
重试是应对死锁的兜底手段,不是解决根因的手段——如果某类操作的死锁重试率持续偏高,说明加锁顺序问题依然存在,应该回头修代码,而不是靠重试机制长期掩盖。
常见坑
- 不同代码路径以不同顺序访问同一批资源:死锁最常见也最容易被忽视的根因。
- 死锁发生后只加重试,不看死锁日志找根因:同类死锁反复出现,只是被重试悄悄掩盖了。
- 批量操作不对涉及记录排序:不同批次任务并发执行时随机构成循环等待。
- 全局使用最严格的隔离级别:不必要地放大了锁范围,死锁概率随之升高。
- 重试逻辑没有次数上限:加锁顺序问题没解决的情况下,无限重试徒增系统负担。
常见问题
死锁是不是说明数据库设计得不好?
不一定。死锁是数据库并发控制机制里必然存在的一种正常处理结果,即使设计良好的系统在高并发下也可能偶发死锁。真正需要警惕的是死锁频率异常偏高或持续增长,这通常指向具体的加锁顺序问题,值得投入时间排查。
应用层可以完全避免死锁吗?
理论上如果所有涉及多资源的操作都严格遵循统一的加锁顺序,可以把死锁概率降到极低,但完全杜绝在复杂系统里很难做到绝对保证。更现实的目标是把死锁频率控制在低水平,并确保应用层有正确的重试机制兜底偶发情况。
死锁和普通的锁等待超时是一回事吗?
不是。锁等待超时是指等待获取锁的时间超过了设定的阈值,可能只是因为持锁事务执行时间长,并不涉及循环等待,超时后通常直接失败但不需要数据库主动介入选择牺牲者;死锁是循环等待,数据库必须主动检测并强制解开。
为什么同样的代码,测试环境很少出现死锁,生产环境却频繁出现?
死锁的触发通常需要足够高的并发度让多个事务恰好在时间上重叠命中同一批资源。测试环境的并发量往往远低于生产,即使代码里存在加锁顺序问题,也很难在低并发下被触发,这也是死锁类问题容易被测试阶段漏掉的原因之一。
资料来源
主流关系型数据库关于事务隔离级别、锁机制与死锁检测的官方文档,以及数据库并发控制领域关于死锁预防的通用工程实践。本文为通用运维说明,仅供参考,具体机制以实际数据库版本与隔离级别配置为准。
相关阅读
备案站点的数据库运维与高可用要点
数据库在备案站点里的位置 备案网站的用户、内容、线索几乎都存在数据库里,是最核心的资产。数据库一旦宕机,站点直接不可用;数据一旦损坏或丢失,损失往往不可逆。所以数据库运维是稳定运营的地基。 高可用:主从与故障切换 主从复制:主库写、从库读,读写分离分担压力。 故障切换:主库异常时能自动或快速切到从库,减少不可用时间。…
备案站点的分库分表架构与运维实践
加索引救不了的问题,分库分表也未必救得了 单表数据量涨到几千万甚至上亿行时,慢查询定位和索引优化的边际收益会急剧下降——不是索引没建对,是这张表本身已经超出了单机数据库能舒服处理的规模。这时候团队往往会条件反射地想到分库分表,但分片是一次架构级别的重构,代价远高于加一个索引,动手前必须想清楚三件事:真的是数据量问题,…
网站访问日志留存与安全合规运维要点
为什么要留日志:这是法定要求 《网络安全法》第二十一条明确要求网络运营者"采取监测、记录网络运行状态、网络安全事件的技术措施,并按照规定留存相关的网络日志不少于六个月"。已完成备案、对外提供服务的网站属于网络运营者范畴,日志留存不是可选项,而是基础合规义务。 应留存哪些日志 | 日志类型 | 记录内容 | 主要用途 …
网站新增域名如何补充接入备案
企业在原有网站基础上新增域名,比如启用新的品牌域名或拼音域名指向同一网站时,不能直接绑定使用,而是需要在原备案基础上补充办理新增域名的接入手续。 新增域名前的准备工作 新增域名前,建议先确认该域名已经完成实名认证,可以通过 Whois查询 核对域名的注册信息和实名状态,避免因域名信息未实名导致接入申请被退回。同时确认…
备案信息年度核查该如何配合应对
部分省份的通信管理部门会对辖区内已备案网站开展年度或不定期核查,核查方式包括系统比对、电话回访、短信确认等,目的是确认备案信息与网站实际运营情况是否一致。 核查通常关注哪些内容 年度核查一般围绕主体信息、网站信息、接入信息三个维度展开,具体可以对照下表自查: | 核查维度 | 常见核查点 | | --- | --- …
备案号在网站上的规范展示与使用要求
网站完成备案只是第一步,备案号在页面上的展示方式同样需要长期维护,很多主体正是因为展示细节不规范,在日常巡查或年度核查中被要求整改。 展示位置与基本要求 通常做法是将备案号放置在网站首页底部,文字需清晰可辨、不被图片或广告遮挡,且格式应与下发的备案号文本保持一致,不能随意增减字符或调整顺序。是否需要加超链接指向查询入…
备案注销之后重新申请要注意什么
有些主体在此前因业务调整、接入商变更等原因注销了原有备案,之后又因新的业务需要重新申请备案。这种“二次备案”与首次备案在流程上大体相似,但有几个环节容易被忽略。 重新申请前的自查 重新申请前,建议先用 ICP备案查询 确认原备案是否确实已经完成注销,避免出现原备案未彻底清空、新申请与旧记录冲突的情况。同时检查计划使用…
备案信息变更有没有时效要求
备案不是办理完成后就一劳永逸的事项,主体信息、网站信息或联系方式一旦发生变化,通常需要在信息变化后的一定时间内完成变更提交,具体时限以属地管局要求为准。 哪些变化需要及时申报 常见需要变更的情形包括:主办单位名称或证件信息变化、网站负责人更换、联系电话或邮箱失效、网站域名调整、网站内容服务类型发生实质变化等。这类变更…