[转]处理MySQL数据库出现大量Locked的一个案例

做为一款轻量级数据库软件,MySQL在使用过程中遇到访问速度慢,或者无法响应这类的问题,解决方式基本都有定式,一般第一反应都会是登录到MySQL, show processlist看看当前连接状态。

虽说简单,但show processlist显示的信息确实是相当有用,有一回,三思收到反馈说MySQL查询很慢,于是,赶紧登录到mysql中,执行show processlist查看当前连接信息:

mysql> show processlist;

+--------+-------------+--------------------+-------+---------+-------+----------------------------------+----------------------------------------------------------------------------------+

| Id | User | Host | db | Command | Time | State | Info |

+--------+-------------+--------------------+-------+---------+-------+----------------------------------+----------------------------------------------------------------------------------+

| 1 | system user | | NULL | Connect | 342266| Waiting for master to send event | NULL |

| 2 | system user | | hdpic | Connect | 872 | Locked | UPDATE a SET STATE=0 WHERE ID=83752 |

| 123890 | hdpic_read | 192.168.1.79:54910 | hdpic | Query | 1512 | Sending data | select z.ID,z.TITLE,z.CREATOR_USER_NICK,z.CREATOR_USER_IDEN,z.LASTEDITOR_TI |

| 124906 | hdpic_read | 192.168.1.39:18844 | hdpic | Query | 845 | Locked | select * from a where ((ID = 78789) AND (STATE != 0)) |

| 124912 | hdpic_read | 192.168.1.39:18862 | hdpic | Query | 845 | Locked | select * from a where ((ID = 16031) AND (STATE != 0)) |

| 124914 | hdpic_read | 192.168.1.39:18865 | hdpic | Query | 837 | Locked | select * from a where ((ID = 39109) AND (STATE != 0)) |

| 124917 | hdpic_read | 192.168.1.39:18875 | hdpic | Query | 833 | Locked | select * from a where ((ID = 16031) AND (STATE != 0)) |


一堆的Locked,怪不得慢啊,阻塞的时间不短了,十几分钟。

通常来说存在Locked就说明当前读写操作存在被阻塞的情况,一般我们看到锁都会下意识认为是由于写阻塞了读,上面的结果看仿佛也符合这一特征:只有一条UPDATE,而无数条的SELECT。猜是必须的,但不能瞎猜,这毕竟是线上系统,就算想杀连接的线程,也是要杀掉造成阻塞的那个,不能把所有Locked的全杀了,不然DBA本人很快也要被人杀了,因此具体情况如何还是需要继续分析。

从show processlist查看到的信息来看,UPDATE的语句是很简单的,分析a的表结构,该表为MyISAM表,ID为该表主键,该条更新应该能够瞬间执行完,即使系统繁忙也不应该,而且通过查看当前的系统状态,整体负载很低,iostat中看I/Owait几可忽略,该写操作不太可能这么长时间都没有执行完。

这个时候再分析show processlist中显示的信息,发现id 123890的语句执行时间最长,肯定是在该UPDATE语句之前执行的,通过show full processlist查看语句详表,看到该查询也访问到了a表,经此分析,应该是该语句长时间的读阻塞了写,而被阻塞的写操作由于处于最优先处理队列,又阻塞了其它的读。

不过这些都还只是我们的推论,考虑到线上系统服务的可靠性,最好还是能找到更确切的证据,而后再做操作。

mysqladmin命令有一个debug参数,可以分析当前MySQL服务的状态信息,同时也可以用来帮助我们定位当前锁的详细情况,这里我们通过该命令分析一下当前MySQL服务的详细状态,执行mysqladmin命令如下:
[code="linux"]
# mysqladmin -ujss -p -S /data/3306/mysql.sock debug
Enter password:
[/code]
debug会将状态信息生成到mysql的错误文件,一般锁的信息都会保存在最后几行,这里我们在操作系统层error log最后几行:
[code="linux"]
# tail -10 phpmysql02.err

Thread database.table_name Locked/Waiting Lock_type


2 hdpic.t_wiki_zutu Waiting - write Highest priority write lock

123890 hdpic.t_wiki_zutu_category Locked - read Low priority read lock

123890 hdpic.t_wiki_zutu_photo Locked - read Low priority read lock

123890 hdpic.t_wiki_zutu Locked - read Low priority read lock
124906 hdpic.t_wiki_zutu Waiting - read Low priority read lock
[/code]

从上述信息可以看出,123890持有的读锁阻塞了2的写入和124906的读操作,这个状态符合我们的推论,接下来处理就比较单纯了,如果现状不可接受,不能继续等待,将123890杀掉,释放资源即可:

mysql> kill 123890;
Query OK, 0 rows affected (0.00 sec)

再次执行show processlist查看:

mysql> show processlist;

+--------+-------------+--------------------+-------+---------+--------+----------------------------------+------------------+

| Id | User | Host | db | Command | Time | State | Info |

+--------+-------------+--------------------+-------+---------+--------+----------------------------------+------------------+

| 1 | system user | | NULL | Connect | 342390 | Waiting for master to send event | NULL |

| 124906 | hdpic_read | 192.168.1.39:18844 | hdpic | Sleep | 1 | | NULL |

| 124912 | hdpic_read | 192.168.1.39:18862 | hdpic | Sleep | 2 | | NULL |

| 124914 | hdpic_read | 192.168.1.39:18865 | hdpic | Sleep | 1 | | NULL |

| 124917 | hdpic_read | 192.168.1.39:18875 | hdpic | Sleep | 1 | | NULL |

| 124919 | hdpic_read | 192.168.1.39:18877 | hdpic | Sleep | 2 | | NULL |


已经没有Locked的连接,此时向前端人员询问,告知响应慢的现象也已经消除,服务恢复正常。
原文转自:http://space.itpub.net/7607759/viewspace-696781
<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、付费专栏及课程。

余额充值