外观
MySQL 死锁告警
死锁检测方法
内置检测机制
InnoDB死锁检测
InnoDB存储引擎会自动检测死锁,当检测到死锁时,会选择一个代价较小的事务进行回滚。
死锁检测算法
InnoDB使用等待图(Wait-For Graph)算法来检测死锁:
- 为每个事务创建一个节点
- 当事务A等待事务B持有的资源时,创建一条从A到B的边
- 如果等待图中存在环,则存在死锁
查看死锁信息
方法1:查看错误日志
bash
# 查看错误日志中的死锁信息
grep -i "deadlock" /path/to/error.log方法2:使用SHOW ENGINE INNODB STATUS
sql
-- 查看InnoDB状态,包含死锁信息
SHOW ENGINE INNODB STATUS\G;方法3:使用Performance Schema
sql
-- 查看死锁相关事件
SELECT * FROM performance_schema.events_waits_current WHERE event_name LIKE '%lock%';
-- 查看事务等待情况
SELECT * FROM performance_schema.data_locks;
SELECT * FROM performance_schema.data_lock_waits;死锁信息解读
典型死锁信息
LATEST DETECTED DEADLOCK
------------------------
2023-12-01 10:00:00 0x7f1234567890
*** (1) TRANSACTION:
TRANSACTION 123456, ACTIVE 10 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 42, OS thread handle 139709834567890, query id 123456 localhost root updating
UPDATE users SET balance = balance - 100 WHERE id = 1;
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 123 page no 4 n bits 72 index PRIMARY of table `test`.`users` trx id 123456 lock_mode X locks rec but not gap waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
0: len 4; hex 80000001; asc ;;
1: len 6; hex 00000001e240; asc @;;
2: len 7; hex b60000019d0110; asc ;;
3: len 4; hex 800003e8; asc ;;
*** (2) TRANSACTION:
TRANSACTION 123457, ACTIVE 8 sec starting index read
mysql tables in use 1, locked 1
2 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 43, OS thread handle 139709834567891, query id 123457 localhost root updating
UPDATE users SET balance = balance + 100 WHERE id = 2;
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 123 page no 4 n bits 72 index PRIMARY of table `test`.`users` trx id 123457 lock_mode X locks rec but not gap
Record lock, heap no 2 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
0: len 4; hex 80000001; asc ;;
1: len 6; hex 00000001e240; asc @;;
2: len 7; hex b60000019d0110; asc ;;
3: len 4; hex 800003e8; asc ;;
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 123 page no 5 n bits 72 index PRIMARY of table `test`.`users` trx id 123457 lock_mode X locks rec but not gap waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
0: len 4; hex 80000002; asc ;;
1: len 6; hex 00000001e241; asc A;;
2: len 7; hex b60000019d0110; asc ;;
3: len 4; hex 800003e8; asc ;;
*** WE ROLL BACK TRANSACTION (1)信息解读
- 事务1:正在执行
UPDATE users SET balance = balance - 100 WHERE id = 1,等待id=1的行锁 - 事务2:正在执行
UPDATE users SET balance = balance + 100 WHERE id = 2,持有id=1的行锁,等待id=2的行锁 - 死锁原因:两个事务相互等待对方持有的锁
- 解决方案:MySQL回滚了事务1
死锁告警配置
监控工具配置
Prometheus + Grafana
配置Prometheus采集
prometheus.yml
yaml
scrape_configs:
- job_name: 'mysql'
static_configs:
- targets: ['localhost:9104']
scrape_interval: 15s配置Grafana告警
告警规则
yaml
groups:
- name: mysql_deadlock_alerts
rules:
- alert: MySQLDeadlockDetected
expr: mysql_global_status_innodb_deadlocks > 0
for: 5m
labels:
severity: warning
annotations:
summary: "MySQL死锁检测"
description: "实例 {{ $labels.instance }} 在过去5分钟内检测到死锁"
- alert: MySQLFrequentDeadlocks
expr: rate(mysql_global_status_innodb_deadlocks[5m]) > 1
for: 10m
labels:
severity: critical
annotations:
summary: "MySQL频繁死锁"
description: "实例 {{ $labels.instance }} 在过去10分钟内频繁发生死锁,速率为 {{ $value }} 次/分钟"MySQL Enterprise Monitor
配置死锁告警
- 登录MySQL Enterprise Monitor
- 导航到"Alerts" → "Manage Rules"
- 找到"Deadlock Detected"规则
- 配置告警阈值和通知方式
开源监控工具
Percona Monitoring and Management (PMM)
- 安装PMM客户端
- 配置MySQL实例监控
- 使用预定义的死锁监控面板
- 配置告警规则
Zabbix
配置监控项
# 监控项键值
mysql.status[Innodb_deadlocks]
# 触发器表达式
{Template App MySQL:mysql.status[Innodb_deadlocks].diff(5m)}>0自定义监控脚本
死锁监控脚本
bash
#!/bin/bash
# MySQL连接信息
HOST="localhost"
PORT="3306"
USER="monitor"
PASSWORD="password"
# 获取死锁数量
DEADLOCKS=$(mysql -h $HOST -P $PORT -u $USER -p$PASSWORD -e "SHOW GLOBAL STATUS LIKE 'Innodb_deadlocks';" | grep Innodb_deadlocks | awk '{print $2}')
# 获取死锁详情
DEADLOCK_DETAILS=$(mysql -h $HOST -P $PORT -u $USER -p$PASSWORD -e "SHOW ENGINE INNODB STATUS\G" | grep -A 50 "LATEST DETECTED DEADLOCK")
# 检查死锁
if [ $DEADLOCKS -gt 0 ]; then
echo "警告: 检测到 $DEADLOCKS 个死锁"
echo "死锁详情:"
echo "$DEADLOCK_DETAILS"
# 发送告警(可集成邮件、短信等)
# mail -s "MySQL死锁告警" admin@example.com <<< "检测到死锁: $DEADLOCKS 个\n$DEADLOCK_DETAILS"
fi
# 输出指标(用于监控系统采集)
echo "mysql_deadlocks $DEADLOCKS"定时执行
bash
# 添加到crontab
*/5 * * * * /path/to/deadlock_monitor.sh >> /var/log/mysql/deadlock_monitor.log 2>&1应用层监控
捕获死锁异常
Java应用示例
java
try {
// 执行事务操作
statement.executeUpdate("UPDATE users SET balance = balance - 100 WHERE id = 1");
} catch (SQLException e) {
// 捕获死锁异常
if (e.getErrorCode() == 1213) { // MySQL死锁错误码
logger.warn("检测到死锁,正在重试...", e);
// 实现重试逻辑
retryTransaction();
} else {
throw e;
}
}Python应用示例
python
import MySQLdb
def execute_with_retry(connection, query, max_retries=3):
retries = 0
while retries < max_retries:
try:
cursor = connection.cursor()
cursor.execute(query)
connection.commit()
return True
except MySQLdb.Error as e:
if e.args[0] == 1213: # 死锁错误码
print(f"检测到死锁,正在重试 ({retries+1}/{max_retries})...")
retries += 1
connection.rollback()
time.sleep(1) # 等待1秒后重试
else:
raise
return False死锁处理策略
紧急处理步骤
步骤1:确认死锁发生
sql
-- 查看死锁数量
SHOW GLOBAL STATUS LIKE 'Innodb_deadlocks';
-- 查看详细死锁信息
SHOW ENGINE INNODB STATUS\G;步骤2:分析死锁原因
- 查看死锁事务:从InnoDB状态输出中分析涉及的事务
- 识别冲突SQL:确定导致死锁的SQL语句
- 分析锁类型:了解使用的锁类型(行锁、表锁等)
- 确定资源争用:识别冲突的资源(行、索引等)
步骤3:打破死锁
MySQL会自动检测并打破死锁,选择一个代价较小的事务进行回滚。但在某些情况下,可能需要手动干预:
sql
-- 查看当前事务
SHOW PROCESSLIST;
-- 终止导致死锁的事务
KILL [process_id];根本解决方案
优化SQL语句
- 减少事务范围:将大事务拆分为多个小事务
- 优化查询条件:确保使用索引,减少锁范围
- 统一访问顺序:多个事务按相同顺序访问资源
- 避免长事务:尽量缩短事务执行时间
- 使用合理的隔离级别:根据业务需求选择合适的隔离级别
示例:优化前的代码
sql
-- 事务1
START TRANSACTION;
UPDATE users SET balance = balance - 100 WHERE id = 1;
UPDATE users SET balance = balance + 100 WHERE id = 2;
COMMIT;
-- 事务2
START TRANSACTION;
UPDATE users SET balance = balance - 100 WHERE id = 2;
UPDATE users SET balance = balance + 100 WHERE id = 1;
COMMIT;示例:优化后的代码
sql
-- 统一访问顺序:先访问id小的记录
START TRANSACTION;
-- 先更新id=1
UPDATE users SET balance = balance - 100 WHERE id = 1;
-- 再更新id=2
UPDATE users SET balance = balance + 100 WHERE id = 2;
COMMIT;调整InnoDB配置
| 参数名 | 描述 | 建议值 | 说明 |
|---|---|---|---|
innodb_deadlock_detect | 启用死锁检测 | ON | 通常建议开启 |
innodb_lock_wait_timeout | 锁等待超时时间 | 50 | 单位秒,根据业务需求调整 |
innodb_rollback_on_timeout | 超时后回滚整个事务 | OFF | 建议保持默认值 |
innodb_autoinc_lock_mode | 自增锁模式 | 2 | 减少自增锁争用 |
配置示例
ini
# my.cnf配置
innodb_deadlock_detect = ON
innodb_lock_wait_timeout = 50
innodb_autoinc_lock_mode = 2应用层优化
- 实现重试机制:捕获死锁异常并自动重试
- 使用乐观锁:通过版本号或时间戳实现乐观锁
- 批量操作拆分:将批量操作拆分为多个小批量操作
- 异步处理:将非关键操作改为异步处理
- 合理使用索引:确保查询使用索引,减少锁范围
乐观锁示例
sql
-- 表结构添加版本号字段
ALTER TABLE users ADD COLUMN version INT DEFAULT 0;
-- 更新操作
UPDATE users SET balance = balance - 100, version = version + 1 WHERE id = 1 AND version = :current_version;
-- 检查更新是否成功
IF affected_rows = 0 THEN
-- 版本冲突,需要重新读取数据
END IF;死锁预防措施
开发规范
- 统一访问顺序:所有事务按相同顺序访问表和行
- 减少事务大小:尽量缩短事务执行时间和范围
- 使用合理的隔离级别:根据业务需求选择隔离级别
- 避免长时间锁定:避免在事务中执行耗时操作
- 合理使用索引:确保查询使用索引,减少锁范围
- 避免循环依赖:设计系统时避免循环依赖关系
运维规范
- 定期监控:监控死锁数量和频率
- 性能优化:定期优化SQL和索引
- 配置调优:根据业务需求调整InnoDB配置
- 容量规划:确保系统资源充足,避免资源争用
- 定期审计:审计应用代码,发现潜在的死锁风险
- 培训教育:培训开发人员了解死锁原理和预防措施
监控指标
| 指标名 | 描述 | 告警阈值 | 监控频率 |
|---|---|---|---|
| Innodb_deadlocks | 死锁数量 | >0 | 5分钟 |
| Innodb_lock_wait_time_avg | 平均锁等待时间 | >100ms | 5分钟 |
| Innodb_lock_waits | 锁等待次数 | >10 | 5分钟 |
| Innodb_row_lock_time_avg | 平均行锁等待时间 | >50ms | 5分钟 |
| Innodb_row_lock_waits | 行锁等待次数 | >50 | 5分钟 |
常见死锁场景与解决方案
场景1:两个事务交叉更新
场景描述
sql
-- 事务1
START TRANSACTION;
UPDATE table1 SET col1 = 1 WHERE id = 1;
UPDATE table2 SET col2 = 1 WHERE id = 1;
COMMIT;
-- 事务2
START TRANSACTION;
UPDATE table2 SET col2 = 1 WHERE id = 1;
UPDATE table1 SET col1 = 1 WHERE id = 1;
COMMIT;解决方案
- 统一访问顺序:两个事务按相同顺序访问表
- 使用分布式锁:对于跨表操作,考虑使用分布式锁
场景2:范围查询与单行更新冲突
场景描述
sql
-- 事务1:范围查询(可能锁定多个行)
START TRANSACTION;
SELECT * FROM users WHERE age > 30 FOR UPDATE;
-- 处理数据
COMMIT;
-- 事务2:单行更新
START TRANSACTION;
UPDATE users SET name = 'New Name' WHERE id = 1;
COMMIT;解决方案
- 使用更精确的查询条件:尽量使用主键或唯一索引进行查询
- 减少锁定范围:只锁定必要的行
- 使用READ COMMITTED隔离级别:减少锁定范围
场景3:外键约束导致的死锁
场景描述
sql
-- 表结构
CREATE TABLE parent (id INT PRIMARY KEY);
CREATE TABLE child (id INT PRIMARY KEY, parent_id INT, FOREIGN KEY (parent_id) REFERENCES parent(id));
-- 事务1
START TRANSACTION;
INSERT INTO child (id, parent_id) VALUES (1, 1);
-- 等待
COMMIT;
-- 事务2
START TRANSACTION;
DELETE FROM parent WHERE id = 1;
-- 等待
COMMIT;解决方案
- 先操作子表:删除时先删除子表数据,再删除父表数据
- 批量操作拆分:将批量删除拆分为多个小批量操作
- 使用级联删除:配置外键为级联删除
场景4:自增锁导致的死锁
场景描述
多个事务同时插入带有自增主键的表,可能会因为自增锁争用而导致死锁。
解决方案
- 调整自增锁模式:设置
innodb_autoinc_lock_mode = 2 - 批量插入:使用批量插入减少自增锁争用
- 预分配自增ID:对于高并发场景,考虑预分配自增ID
场景5:间隙锁导致的死锁
场景描述
在REPEATABLE READ隔离级别下,InnoDB会使用间隙锁来防止幻读,可能导致死锁。
sql
-- 事务1
START TRANSACTION;
SELECT * FROM users WHERE age BETWEEN 20 AND 30 FOR UPDATE;
-- 等待
COMMIT;
-- 事务2
START TRANSACTION;
INSERT INTO users (id, age) VALUES (10, 25);
-- 等待
COMMIT;解决方案
- 使用READ COMMITTED隔离级别:减少间隙锁的使用
- 使用更精确的查询条件:尽量使用唯一索引进行查询
- 避免范围查询加锁:如果可能,避免使用范围查询加锁
死锁分析工具
内置工具
SHOW ENGINE INNODB STATUS
最基本的死锁分析工具,提供详细的死锁信息。
Performance Schema
提供更详细的锁相关信息,包括锁类型、锁定时间等。
sql
-- 查看锁等待情况
SELECT * FROM performance_schema.data_lock_waits;
-- 查看持有的锁
SELECT * FROM performance_schema.data_locks;
-- 查看等待事件
SELECT * FROM performance_schema.events_waits_current WHERE event_name LIKE '%lock%';第三方工具
Percona Toolkit
pt-deadlock-logger
bash
# 安装Percona Toolkit
sudo apt-get install percona-toolkit
# 使用pt-deadlock-logger监控死锁
pt-deadlock-logger --host=localhost --user=root --password=passwordpt-query-digest
bash
# 分析慢查询日志中的死锁相关信息
pt-query-digest /path/to/slow.log | grep -A 20 "Deadlock"MySQL Enterprise Monitor
提供可视化的死锁监控面板,实时显示死锁情况。
PMM (Percona Monitoring and Management)
开源监控解决方案,包含死锁监控功能。
常见问题(FAQ)
Q1: 如何快速识别死锁问题
A1: 识别死锁的方法:
- 查看错误日志:错误日志中会记录死锁详细信息
- 使用SHOW ENGINE INNODB STATUS:查看InnoDB状态输出中的死锁信息
- 监控指标:监控
Innodb_deadlocks状态变量 - 应用异常:捕获应用中的死锁异常(错误码1213)
- 性能下降:突然的性能下降可能是死锁导致的
Q2: 死锁发生后应该如何处理
A2: 处理步骤:
- 保持冷静:MySQL会自动检测并处理死锁
- 查看详情:分析死锁日志,了解死锁原因
- 优化SQL:根据死锁原因优化SQL语句
- 调整代码:统一访问顺序,减少事务范围
- 监控告警:设置死锁告警,及时发现问题
- 避免手动干预:除非必要,否则不要手动终止事务
Q3: 如何预防死锁的发生
A3: 预防措施:
- 统一访问顺序:所有事务按相同顺序访问表和行
- 减少事务大小:将大事务拆分为多个小事务
- 使用合理的隔离级别:根据业务需求选择隔离级别
- 优化查询条件:确保使用索引,减少锁范围
- 避免长时间锁定:避免在事务中执行耗时操作
- 使用乐观锁:对于并发冲突较多的场景,使用乐观锁
Q4: 高并发场景下如何优化死锁处理
A4: 高并发优化策略:
- 实现重试机制:捕获死锁异常并自动重试
- 使用队列:通过队列削峰,控制并发数量
- 预热缓存:减少事务中的数据库访问
- 批量操作拆分:将批量操作拆分为多个小批量操作
- 调整配置:合理设置
innodb_lock_wait_timeout和innodb_deadlock_detect - 监控预警:设置死锁频率预警,及时发现问题
Q5: 死锁与索引有什么关系
A5: 关系分析:
- 索引减少锁范围:合理的索引可以减少锁定的行数
- 全表扫描增加死锁风险:全表扫描会锁定更多行,增加死锁概率
- 索引类型影响:不同类型的索引对锁的影响不同
- 最佳实践:
- 为经常查询的字段创建索引
- 避免在索引列上使用函数
- 使用覆盖索引减少锁竞争
- 定期优化和重建索引
Q6: 如何监控死锁情况
A6: 监控方法:
- Prometheus + Grafana:配置死锁监控指标和告警
- MySQL Enterprise Monitor:使用官方监控工具
- PMM:Percona Monitoring and Management
- 自定义脚本:定期检查死锁状态并记录
- 应用层监控:捕获并记录应用中的死锁异常
- 告警阈值:
- 轻微:每小时1-5次死锁
- 中等:每小时5-20次死锁
- 严重:每小时20+次死锁
Q7: 死锁检测会影响数据库性能吗
A7: 性能影响分析:
- 死锁检测开销:死锁检测会消耗CPU资源
- 高并发影响:在数千个并发事务时,死锁检测可能成为瓶颈
- 优化建议:
- 如果系统几乎不会发生死锁,可考虑关闭死锁检测
- 设置合理的
innodb_lock_wait_timeout作为 fallback - 定期分析死锁日志,从根本上解决死锁问题
- 对于超高并发系统,可考虑应用层设计避免死锁
Q8: 如何分析死锁日志
A8: 日志分析方法:
- 错误日志:找到"LATEST DETECTED DEADLOCK"部分
- InnoDB状态:使用
SHOW ENGINE INNODB STATUS查看 - 关键信息:
- 涉及的事务ID
- 执行的SQL语句
- 等待的锁类型
- 持有的锁
- 回滚的事务
- 分析工具:使用Percona Toolkit的
pt-deadlock-logger - 模式识别:识别重复发生的死锁模式,从根本上解决
Q9: 如何在开发中避免死锁
A9: 开发建议:
- 编码规范:制定统一的数据库访问规范
- 单元测试:编写并发测试用例
- 代码审查:审查代码中的并发处理逻辑
- 培训教育:提高开发人员对死锁的认识
- 工具支持:使用静态分析工具检测潜在的死锁风险
- 最佳实践:
- 使用短事务
- 统一访问顺序
- 避免在事务中调用外部服务
- 合理使用锁粒度
Q10: 死锁与事务隔离级别有什么关系
A10: 关系分析:
- READ UNCOMMITTED:不会发生死锁(但会有其他问题)
- READ COMMITTED:减少死锁概率(间隙锁使用较少)
- REPEATABLE READ:默认级别,可能发生死锁
- SERIALIZABLE:最高隔离级别,死锁概率最高
- 选择建议:
- 一般场景使用默认的REPEATABLE READ
- 高并发场景考虑使用READ COMMITTED
- 数据一致性要求高的场景使用SERIALIZABLE
案例分析
案例1:电商系统库存扣减死锁
背景
- 高并发电商系统
- 秒杀场景,大量用户同时抢购商品
- 库存扣减逻辑:先检查库存,再扣减库存
- 死锁频率:高峰期每分钟10+次死锁
代码分析
sql
-- 原始代码
START TRANSACTION;
-- 检查库存
SELECT stock FROM products WHERE id = 1 FOR UPDATE;
-- 扣减库存
UPDATE products SET stock = stock - 1 WHERE id = 1;
COMMIT;死锁原因
- 多个事务同时执行相同的操作序列
- 检查库存时锁定了行
- 扣减库存时需要等待其他事务释放锁
- 形成循环等待
解决方案
- 优化SQL:合并检查和更新操作
sql
-- 优化后代码
UPDATE products SET stock = stock - 1 WHERE id = 1 AND stock > 0;
-- 检查更新是否成功
IF affected_rows = 0 THEN
-- 库存不足
END IF;应用层优化:
- 实现重试机制
- 使用队列削峰
- 预热库存缓存
配置优化:
iniinnodb_autoinc_lock_mode = 2 innodb_lock_wait_timeout = 30
优化效果
- 死锁频率降低95%
- 系统吞吐量提升30%
- 用户体验显著改善
案例2:金融系统转账死锁
背景
- 金融转账系统
- 转账逻辑:从账户A扣款,向账户B存款
- 并发量:每秒100+笔转账
- 死锁问题:频繁出现转账死锁
代码分析
sql
-- 事务1:A向B转账
START TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE id = 1; -- 账户A
UPDATE accounts SET balance = balance + 100 WHERE id = 2; -- 账户B
COMMIT;
-- 事务2:B向A转账
START TRANSACTION;
UPDATE accounts SET balance = balance - 50 WHERE id = 2; -- 账户B
UPDATE accounts SET balance = balance + 50 WHERE id = 1; -- 账户A
COMMIT;死锁原因
- 两个事务交叉更新账户表
- 事务1持有账户A的锁,等待账户B的锁
- 事务2持有账户B的锁,等待账户A的锁
- 形成循环等待
解决方案
- 统一访问顺序:
sql
-- 优化后代码:按账户ID顺序更新
START TRANSACTION;
-- 先更新ID较小的账户
IF from_id < to_id THEN
UPDATE accounts SET balance = balance - amount WHERE id = from_id;
UPDATE accounts SET balance = balance + amount WHERE id = to_id;
ELSE
UPDATE accounts SET balance = balance + amount WHERE id = to_id;
UPDATE accounts SET balance = balance - amount WHERE id = from_id;
END IF;
COMMIT;- 使用存储过程:
sql
DELIMITER //
CREATE PROCEDURE transfer(from_id INT, to_id INT, amount DECIMAL(10,2))
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE retries INT DEFAULT 0;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET done = 1;
WHILE retries < 3 AND NOT done DO
START TRANSACTION;
-- 按ID顺序更新
IF from_id < to_id THEN
UPDATE accounts SET balance = balance - amount WHERE id = from_id AND balance >= amount;
IF ROW_COUNT() = 0 THEN
ROLLBACK;
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Insufficient balance';
END IF;
UPDATE accounts SET balance = balance + amount WHERE id = to_id;
ELSE
UPDATE accounts SET balance = balance + amount WHERE id = to_id;
UPDATE accounts SET balance = balance - amount WHERE id = from_id AND balance >= amount;
IF ROW_COUNT() = 0 THEN
ROLLBACK;
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Insufficient balance';
END IF;
END IF;
COMMIT;
SET done = 1;
END WHILE;
IF retries >= 3 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Transfer failed after multiple attempts';
END IF;
END //
DELIMITER ;优化效果
- 死锁完全消除
- 转账成功率提升至99.99%
- 系统稳定性显著提高
