mysql rec gap_记一次mysql死锁解析

本文详细分析了一次由gap锁和插入意向锁引起的MySQL死锁情况。事务在尝试插入数据时,因加锁顺序不同导致双方等待对方释放gap锁,从而形成死锁。死锁涉及RR隔离级别下的GAP锁机制,防止幻读但可能影响并发。选择RC或RR隔离级别需考虑业务需求。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

从日志中我们可以看出死锁发生在表T_ORDERBILL_TASK上而且是两条相同类型的针对表T_ORDERBILL_TASK插入的insert语句!检查T_ORDERBILL_TASK表结构如下:

136be34314a57ba454d97dfa8ef5d707.png

可见ORG_CODE上有二级索引。

(1) TRANSACTION也就是第一个事务的信息中:

lWAITING FOR THIS LOCK TO BE GRANTED,表示的是这个事务在等待的锁信息;

lindex ORG_CODE of table `T_BSE_QUERY_SCHEME`.`T_ORDERBILL_TASK`表示等的是表T_ORDERBILL_TASK上ORG_CODE索引上面的锁;

llock_mode X locks gap before rec insert intention waiting Record lock 表示这个语句要加一个插入意向锁但是还在等待状态,Record lock说明这是一个记录锁。

(2) TRANSACTION 也就是第二个事务中:

l(2) HOLDS THE LOCK(S)中lock_mode X locks gap before rec 表示了持有GAP锁

l(2) WAITING FOR THIS LOCK TO BE GRANTED中lock_mode X locks gap before rec insert intention waiting同(1)TRANSACTION等待加插入意向锁

结合业务开发提供的该模块儿的业务逻辑和发生死锁时间上下文业务日志可以整理出事务1和事务2的执行顺序为:

136be34314a57ba454d97dfa8ef5d707.png

或者

136be34314a57ba454d97dfa8ef5d707.png

可以看出在插入一条数据前先执行当前读锁住该条数据,若不存在则插入该条数据,否则执行update。

由于索引是顺序存储的,查出110026介于110019和110036之间:

由此可知:

当session 1执行select … for update当前读,由于ORG_CODE = 110026的数据不存在,因此会加上gap锁(110019,110036)。

Session 2执行select … for update当前读,同样也会加上gap锁(110019,110036),由于gap锁不互斥,所以该语句也可成功执行。

但是到第三步时无论是session 1先插入还是session 2先插入都会等待对方的gap锁,第四步时后插入的也会被对方的gap锁阻塞。两个session进入互相等待状态形成死锁,最后死锁检测将session2回滚。

[

结论

]

其实GAP锁,就是RR隔离级别相对于RC隔离级别不会出现幻读的关键。在RR隔离级别下,条件列上为非唯一索引时,当前读通过条件列未定位到满足条件的记录时会加GAP锁,保证后续的insert不能插入相同值的数据,以防止出现幻读。需要注意的是GAP锁并不互斥但是和插入意向锁互斥。相同语句的情况下RR模式由于会加GAP锁可能锁住更多的数据,虽然防止了幻读,但是会影响一定的并发。在配置RC和RR隔离级别的时候一定要根据业务场景进行选择。

<think>好的,用户现在问的是MySQL死锁录。之前他们问过Windows的内存和CPU告警录,现在转向数据库的死锁,可能是在系统监控之后遇到了数据库层面的问题,或者是想全面了解不同系统的故障排查方法。 首先,我需要确定用户的需求是什么。他们可能遇到了死锁问题,需要知道如何检测和分析,或者是想提前了解如何监控。用户可能是开发人员、DBA或者运维人员,需要具体的操作步骤和工具。 接下来,我需要回忆MySQL死锁相关的知识点。死锁发生的条件,如何录,如何查看日志,以及如何分析解决。MySQL的InnoDB引擎有死锁检测机制,会回滚其中一个事务,并录到错误日志。需要确认日志的开启状态和位置。 用户可能需要知道如何启用死锁日志,查看最近的死锁信息,以及分析这些信息的方法。还要考虑不同MySQL版本的差异,比如5.6之后才有innodb_print_all_deadlocks参数,可以录所有死锁到错误日志,而不仅限于最后一个。 另外,用户可能对如何解读死锁日志中的内容不太清楚,需要解释TRANSACTION、WAITING FOR锁、HEX信息等。还要提到使用SHOW ENGINE INNODB STATUS的命令来获取最近的死锁信息,但可能只保留最后一个死锁的情况。 还需要考虑如何预防死锁,比如事务设计、索引优化、锁定顺序等,但用户的问题主要是录和分析,所以这部分可能需要简要提及,但重点还是在录和排查步骤。 可能用户在实际操作中遇到了困难,比如日志没有开启,或者看不懂输出的信息,所以需要详细说明如何配置参数,以及如何逐步分析日志中的内容。同时,提醒用户定期监控日志,设置报警,避免死锁影响系统性能。 最后,可能需要推荐一些工具,比如pt-deadlock-logger,或者监控系统集成,帮助用户自动化死锁监控和分析。确保回答结构清晰,步骤明确,让用户能一步步跟着操作。</think>以下是关于 **MySQL 死锁录** 的详细解析,涵盖监控、日志分析及解决方案: --- ### **一、死锁的基本原理** MySQL 的 InnoDB 存储引擎通过**自动检测机制**处理死锁: - 触发条件:两个或多个事务互相持有对方需要的锁。 - 处理方式:InnoDB 会强制回滚其中一个事务(通常选择回滚代价更小的事务),并抛出错误 `ERROR 1213 (40001): Deadlock found`。 --- ### **二、死锁录的存储位置** #### 1. **错误日志(Error Log)** - **默认路径**:`${datadir}/hostname.err`(可通过 `SHOW VARIABLES LIKE 'log_error';` 查询) - **录内容**: ```log LATEST DETECTED DEADLOCK ------------------------ 2024-01-01 12:00:00 0x7f8e3c123700 *** (1) TRANSACTION: TRANSACTION 12345678, ACTIVE 5 sec starting index read mysql tables in use 1, locked 1 LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s) MySQL thread id 100, OS thread handle 123456, query id 987654 localhost root updating UPDATE table1 SET col1 = 1 WHERE id = 10 *** (1) WAITING FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 789 page no 3 n bits 72 index PRIMARY of table `test`.`table1` trx id 12345678 lock_mode X locks rec but not gap waiting *** (2) TRANSACTION: TRANSACTION 87654321, ACTIVE 3 sec starting index read mysql tables in use 1, locked 1 3 lock struct(s), heap size 1136, 2 row lock(s) MySQL thread id 101, OS thread handle 654321, query id 987655 localhost root updating UPDATE table2 SET col2 = 2 WHERE id = 20 *** (2) HOLDS THE LOCK(S): RECORD LOCKS space id 789 page no 4 n bits 72 index PRIMARY of table `test`.`table2` trx id 87654321 lock_mode X locks rec but not gap *** WE ROLL BACK TRANSACTION (2) ``` - **关键字段解释**: - `TRANSACTION`:事务ID及状态。 - `LOCK WAIT`:事务等待的锁类型(如 `X` 排他锁、`S` 共享锁)。 - `RECORD LOCKS`:锁定的录位置(通过 `space id` 和 `page no` 定位)。 #### 2. **InnoDB 状态信息** - 通过命令获取实时死锁信息: ```sql SHOW ENGINE INNODB STATUS\G ``` - **注意**:默认仅保留**最后一次死锁录**。 --- ### **三、配置死锁日志录** #### 1. **启用完整死锁日志** - 修改配置文件 `my.cnf`: ```ini [mysqld] innodb_print_all_deadlocks = ON # 录所有死锁到错误日志(MySQL 5.6+) log_error_verbosity = 3 # 录详细错误信息 ``` - 重启 MySQL 生效。 #### 2. **监控工具** - **pt-deadlock-logger**(Percona Toolkit): ```bash pt-deadlock-logger --user=root --password=xxx --socket=/tmp/mysql.sock ``` - **性能模式(Performance Schema)**: ```sql SELECT * FROM performance_schema.data_locks; -- 查看当前锁状态 SELECT * FROM performance_schema.events_statements_history; -- 关联SQL语句 ``` --- ### **四、死锁分析步骤** #### 1. **定位死锁事务** - 从错误日志中提取死锁信息,重点关注: - **竞争的资源**:锁定录的具体位置(`space id` + `page no`)。 - **事务的SQL语句**:引发死锁的 `UPDATE`/`SELECT ... FOR UPDATE` 等操作。 #### 2. **复现与调试** - 使用 `innodb_lock_monitor`(已弃用,推荐通过 Performance Schema 替代): ```sql CREATE TABLE innodb_lock_monitor (id INT) ENGINE=InnoDB; -- 传统方式启用锁监控 ``` - 通过 `EXPLAIN` 分析SQL执行计划,检查索引使用情况。 #### 3. **代码层优化** - **锁定顺序**:确保所有事务按相同顺序访问表或行。 - **事务粒度**:减少事务执行时间,避免长事务。 - **索引优化**:添加合适的索引,减少全表扫描导致的锁冲突。 --- ### **五、典型死锁案例** #### **案例1:交叉更新导致死锁** - **场景**: ```sql -- 事务A UPDATE table1 SET col1 = 1 WHERE id = 10; UPDATE table2 SET col2 = 2 WHERE id = 20; -- 事务B UPDATE table2 SET col2 = 2 WHERE id = 20; UPDATE table1 SET col1 = 1 WHERE id = 10; ``` - **解决**:统一事务中表的操作顺序。 #### **案例2:Gap锁冲突** - **场景**:范围查询(如 `WHERE id BETWEEN 10 AND 20`)在 `REPEATABLE READ` 隔离级别下产生Gap锁。 - **解决**:改用 `READ COMMITTED` 隔离级别或优化查询条件。 --- ### **六、高级工具推荐** 1. **Percona Toolkit** - `pt-deadlock-logger`:实时死锁事件到数据库表。 2. **MySQL Enterprise Monitor** - 图形化展示死锁频率及影响范围。 3. **Prometheus + Grafana** - 通过 `mysql_exporter` 监控 `innodb_deadlocks` 指标。 --- ### **七、预防策略** 1. **事务设计**: - 使用 `SELECT ... FOR UPDATE NOWAIT`(MySQL 8.0+)避免等待锁。 - 尽量使用基于主键或唯一索引的条件更新。 2. **监控告警**: - 定期扫描错误日志,设置阈值告警(如每分钟超过3次死锁触发通知)。 3. **压力测试**: - 使用 `sysbench` 或 `jmeter` 模拟高并发场景,提前暴露潜在死锁。 --- 通过上述方法,可有效录、分析并减少 MySQL 死锁问题。对于高频死锁场景,建议结合**应用层重试机制**(如 exponential backoff)提升系统容错能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值