现象描述
TDSQL Boundless 实例在运行过程中,业务侧出现死锁报错,可能伴随以下现象:
应用程序日志中出现死锁错误,错误码为
1213(ER_LOCK_DEADLOCK)。应用程序日志中出现锁等待超时错误,错误码为
1205(ER_LOCK_WAIT_TIMEOUT)。实例监控中 SQL 失败数指标升高。
高并发场景下,部分事务被自动回滚并提示 Deadlock found when trying to get lock。
您可在 TDSQL Boundless 控制台的实例详情 > 监控告警 > 指标监控页面查看 SQL 失败数、活跃线程数等指标,判断死锁发生频率和时间点。
可能原因
死锁通常由多个事务相互持有对方需要的锁资源,形成等待环导致。可能原因如下:
序号 | 可能原因 |
1 | 多个事务以不同顺序访问相同的表或行,形成锁等待环。 |
2 | 事务中执行时间过长,持有锁的时间过长,增加死锁概率。 |
3 | 并发量突增,锁竞争加剧,短时间内大量事务争抢同一批资源。 |
4 | 死锁检测未开启或锁等待超时设置不合理,导致死锁无法被及时检测和解除。 |
解决思路
针对不同原因,解决思路如下:
可能原因 | 解决思路 |
事务访问顺序不一致 | 统一事务中表的访问顺序,确保并发事务按相同顺序加锁。 |
事务执行时间过长 | 拆分大事务为小事务,减少单事务持锁时间;优化事务内 SQL 执行效率。 |
并发量突增 | 业务侧进行限流或错峰执行,降低并发争抢强度。 |
死锁检测未开启 | 通过控制台开启死锁检测,并合理设置锁等待超时时间。 |
处理步骤
步骤1:查看监控指标
1. 登录 TDSQL Boundless 控制台,在实例列表中单击目标实例 ID,进入实例详情页。
2. 选择监控告警 > 指标监控页签,查看以下指标的变化趋势:
监控指标 | 说明 | 关注点 |
SQL 失败数 | 每秒失败的 SQL 数量 | 死锁发生时,失败数会突增 |
活跃线程数 | 当前活跃线程数 | 并发量是否突增 |
慢查询数 | 每秒慢查询数量 | 锁等待是否导致查询变慢 |
CPU 利用率 | 实例 CPU 使用率 | 锁竞争是否导致 CPU 升高 |
重点关注 SQL 失败数突增的时间点,与业务并发高峰或批量任务执行时间是否吻合。
步骤2:查看死锁信息
通过系统视图查看死锁记录,分析死锁发生的事务和锁等待关系。
1. 连接数据库实例,执行以下 SQL 查看死锁记录。
SELECTrollback_trans_id,cycle_transactions,occurred_atFROM information_schema.TDSTORE_PESSIMISTIC_DEADLOCK_INFOORDER BY occurred_at DESCLIMIT 20;
该视图展示最近发生的死锁信息,字段说明如下:
rollback_trans_id:被回滚的事务 ID。cycle_transactions:构成死锁环的事务列表。occurred_at:死锁发生时间。2. 如需查看死锁详情,执行以下 SQL 查看死锁的锁等待详情。
SELECTrollback_trans_id,requesting_node,requesting_trans_id,blocking_trans_id,req_lock_range,blk_lock_range,occurred_at,requesting_sqlFROM information_schema.TDSTORE_PESSIMISTIC_DEADLOCK_DETAIL_INFOORDER BY occurred_at DESCLIMIT 20
该视图展示死锁发生时的锁等待关系,帮助您定位冲突的具体资源和事务。
步骤3:查看当前锁等待会话
若死锁正在发生或存在长时间锁等待,查看当前会话的锁等待情况。
1. 执行以下 SQL 查看当前正在执行的会话,重点关注
TIME 值较大的会话,这些会话可能正在等待锁资源。SELECTID,USER,HOST,DB,COMMAND,TIME,STATE,INFOFROM information_schema.PROCESSLISTWHERE COMMAND != 'Sleep'ORDER BY TIME DESCLIMIT 20
2. 若发现长时间锁等待的会话,可使用
KILL 命令终止该会话以释放锁资源。KILL <会话ID>;
警告:
KILL 操作会中断正在执行的事务并触发回滚,请确认目标会话后再执行,避免误杀正常业务会话。步骤4:开启死锁检测
若死锁检测未开启,建议通过控制台开启,使系统自动检测并解除死锁。
1. 登录 TDSQL Boundless 控制台,在实例列表中单击目标实例 ID,进入实例详情页。
2. 选择参数设置 > 数据库参数页签,查找以下参数:
参数名 | 说明 | 默认值 |
tdstore_deadlock_detect | 是否开启死锁检测。开启后系统会自动检测死锁环并回滚其中一个事务。 | OFF |
tdstore_deadlock_victim | 死锁发生时选择牺牲事务的策略。 WRITE_LEAST 优先回滚写数据量较少的事务;START_LATEST 优先回滚较晚开启的事务。 | WRITE_LEAST |
3. 将
tdstore_deadlock_detect 的值修改为 ON,开启死锁检测。4. 根据业务场景设置
tdstore_deadlock_victim 的值:WRITE_LEAST:优先保护写数据量大的事务,适合以数据写入为主的场景。START_LATEST:优先保护先开启的事务,适合事务执行顺序明确的场景。5. 在目标参数所在行的参数当前值列,单击修改参数值,单击保存,在确认对话框中单击确定。
注意:
开启死锁检测会带来少量的性能开销,但在死锁频发的场景下,开启检测可以避免事务长时间阻塞,整体收益大于开销。
步骤5:调整锁等待超时时间
若锁等待超时时间设置不合理(过长或过短),可通过控制台调整
tdsql_lock_wait_timeout 参数。1. 在参数设置 > 数据库参数页签,查找
tdsql_lock_wait_timeout 参数。2. 根据业务场景调整参数值:
默认值为10秒。若业务事务执行时间较长,可适当调大,避免正常事务因锁等待被误杀。
若希望死锁和锁等待尽快被解除,可适当调小,减少业务阻塞时间。
3. 在目标参数所在行的参数当前值列,单击修改参数值,单击保存,在确认对话框中单击确定。
步骤6:优化业务事务
从业务层面优化事务设计,从根本上减少死锁发生概率。
1. 统一访问顺序:确保所有并发事务以相同的顺序访问表和行。例如,事务 A 和事务 B 都先更新表1再更新表2,避免交叉等待。
2. 缩短事务范围:
将大事务拆分为多个小事务,减少单事务持锁时间。
事务中避免包含耗时操作(如远程调用、文件 IO),尽快提交事务。
使用
SELECT ... FOR UPDATE 时,尽量只锁定需要的行,避免锁定过多资源。3. 降低并发强度:
对高并发写入场景进行限流,控制同时执行的事务数量。
批量操作分批执行,避免单批次操作锁定大量资源。
将非核心业务的写入操作错峰执行。
4. 使用合适的索引:
确保更新和删除操作使用索引定位行,避免全表扫描加锁。
无索引的更新操作会锁定大量行,大幅增加死锁概率。
步骤7:优化 SQL 语句
锁竞争往往与低效 SQL 有关,优化 SQL 可以减少锁持有时间。
1. 对涉及锁竞争的 SQL 执行
EXPLAIN 查看执行计划。EXPLAIN UPDATE table_name SET ... WHERE ...;
2. 重点关注以下信息:
type 列为 ALL 表示全表扫描,会锁定大量行,需要添加索引。rows 列值过大说明扫描行数过多,锁定的行也越多。3. 优化建议:
为
WHERE 条件列添加索引,减少扫描和锁定行数。避免在没有索引的列上执行
UPDATE 或 DELETE。使用
LIMIT 限制单次操作影响的行数。预防措施
为避免死锁问题反复发生,建议采取以下预防措施:
开启死锁检测:确保
tdstore_deadlock_detect 参数设置为 ON,使系统自动检测并解除死锁。配置告警策略:在控制台为 SQL 失败数指标配置告警,及时发现死锁问题。
规范事务设计:制定事务开发规范,统一表访问顺序,控制事务大小和持锁时间。
定期巡检慢 SQL:通过 DBbrain 智能诊断定期检查慢 SQL,优化低效查询以减少锁持有时间。
监控 SQL 失败数:定期关注控制台监控面板中的 SQL 失败数指标,若出现突增,及时排查是否存在死锁或锁等待问题。