mysql死锁问题

我的具体情况是:使用RobotFramework测试时,本来可以通过的一个case报错了,报错为:InternalError: (1205, u'Lock wait timeout exceeded; try restarting transaction。网上找了很多也没解决问题,还是这篇文章简单有效。
2016-10-12更新:找到问题所在了,就是线上开发环境和开本地环境同时跑AT,结果因为争抢数据库资源导致数据库死锁。解决方法其实可以简化为两步:1是查出锁死的数据库线程SELECT trx_mysql_thread_id FROM information_schema.INNODB_TRX;;2是将查出的线程杀死 kill 。


前言:朋友咨询我说执行简单的update语句失效,症状如下:
mysql> update order_info set province_id=15 ,city_id= 1667 where order_from=10 and order_out_sn='1407261241xxxx';
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql>

QQ远程过去,开始check

1,查看数据库的隔离级别:

mysql> select @@tx_isolation;
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set (0.00 sec)

mysql>

2,去查看先当前库的线程情况:

mysql> show processlist;
+----------+-----------------+-------------------+-----------------+-------------+---------+-------------------------+-----------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----------+-----------------+-------------------+-----------------+-------------+---------+-------------------------+-----------------------+
| 1 | event_scheduler | localhost | NULL | Daemon | 9635385 | Waiting on empty queue | NULL |
| 9930577 | business_web | 192.168.1.21:45503 | business_db | Sleep | 153 | | NULL |
| 9945825 | business_web | 192.168.1.25:49518 | business_db | Sleep | 43 | | NULL |
| 9946322 | business_web | 192.168.1.23:44721 | business_db | Sleep | 153 | | NULL |
| 9960167 | business_web | 192.168.3.28:2409 | business_db | Sleep | 93 | | NULL |
| 9964484 | business_web | 192.168.1.21:24280 | business_db | Sleep | 7 | | NULL |
| 9972499 | business_web | 192.168.3.28:35752 | business_db | Sleep | 13 | | NULL |
| 10000117 | business_web | 192.168.3.28:9149 | business_db | Sleep | 6 | | NULL |
| 10002523 | business_web | 192.168.3.29:42872 | business_db | Sleep | 6 | | NULL |
| 10007545 | business_web | 192.168.1.21:51379 | business_db | Sleep | 155 | | NULL |
......
+----------+-----------------+-------------------+-----------------+-------------+---------+-------------------------+-----------------------+

没有看到正在执行的慢SQL记录线程,再去查看innodb的事务表INNODB_TRX,看下里面是否有正在锁定的事务线程,看看ID是否在show full processlist里面的sleep线程中,如果是,就证明这个sleep的线程事务一直没有commit或者rollback而是卡住了,我们需要手动kill掉。

mysql> SELECT * FROM information_schema.INNODB_TRX;
*************************** 1. row ***************************
trx_id: 20866
trx_state: LOCK WAIT
trx_started: 2014-07-31 10:42:35
trx_requested_lock_id: 20866:617:3:3
trx_wait_started: 2014-07-30 10:42:35
trx_weight: 2
trx_mysql_thread_id: 9930577
trx_query: delete from dltask where id=1
trx_operation_state: starting index read
trx_tables_in_use: 1
trx_tables_locked: 1
trx_lock_structs: 2
trx_lock_memory_bytes: 376
trx_rows_locked: 1
trx_rows_modified: 0
trx_concurrency_tickets: 0
trx_isolation_level: READ COMMITTED
trx_unique_checks: 1
trx_foreign_key_checks: 1
trx_last_foreign_key_error: NULL
trx_adaptive_hash_latched: 0
trx_adaptive_hash_timeout: 10000
trx_is_read_only: 0
trx_autocommit_non_locking: 0

3,看到有这条9930577的sql,kill掉,执行kill 9930577;

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

mysql>

然后再去查询INNODB_TRX表,就没有阻塞的事务sleep线程存在了,如下所示:
mysql> SELECT * FROM INNODB_TRX;
Empty set (0.00 sec)

ERROR:
No query specified

mysql>
再去执行update语句,就能正常执行了,如下所示:
mysql> update order_info set province_id=15 ,city_id= 1667 where order_from=10 and order_out_sn='1407261241xxxx';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0

mysql>

4,总结分析

表数据量也不大,按照普通的情况来说,简单的update应该不会造成阻塞的,mysql都是autocommit,不会出现update卡住的情况,去查看下autocommit的值。
mysql> select @@autocommit;
+--------------+
| @@autocommit |
+--------------+
| 0 |
+--------------+
1 row in set (0.00 sec)

mysql>

看到亮闪闪的0,这个设置导致原来的update语句如果没有commit的话,你再重新执行update语句,就会等待锁定,当等待时间过长的时候,就会报ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction的错误。
所以赶紧commit刚才执行的update语句,之后 set global autocommit=1;

<think>我们正在解决MySQL死锁问题。根据引用内容,我们可以总结出以下解决方法和最佳实践:1.监控和识别死锁:使用`SHOWENGINEINNODBSTATUS`命令查看死锁信息(引用[1][5])。2.开启死锁监控:在配置文件中设置`innodb_print_all_deadlocks=ON`,将所有死锁记录到错误日志(引用[5])。3.优化事务逻辑:尽量缩小事务范围,减少锁的持有时间;避免大事务(引用[1][4][5])。4.编写高效的SQL:使用索引,减少全表扫描;避免批量操作中的间隙锁问题(引用[3][4])。5.调整隔离级别:根据业务需要,适当降低隔离级别(如从可重复读改为读已提交),减少锁冲突(引用[5])。6.在应用程序中实现重试机制:当捕获到死锁异常时,自动重试事务(引用[2])。7.注意锁的顺序:在程序中保证多个事务对资源的访问顺序一致,避免循环等待(引用[2][4])。现在,我们将这些方法组织成结构清晰的回答。</think>### 如何解决MySQL中的死锁问题(最佳实践) MySQL死锁是指两个或多个事务相互等待对方释放锁资源,导致系统僵持的现象。以下是经过验证的解决方案和最佳实践: #### 一、主动监控与诊断 1. **实时检测死锁** 使用命令查看最新死锁信息: ```sql SHOW ENGINE INNODB STATUS\G ``` 重点关注输出中的`LATEST DETECTED DEADLOCK`段,分析涉及的事务、SQL及锁类型(记录锁/间隙锁等)[^5]。 2. **开启持续监控** 在`my.cnf`配置: ```ini innodb_print_all_deadlocks = ON ``` 所有死锁事件将记录到错误日志,便于长期跟踪[^5]。 #### 二、事务优化策略 1. **缩小事务范围** - 避免单个事务包含过多SQL操作 - 将大事务拆分为小事务,减少锁持有时间 *示例:批量更新改为分批提交*[^4][^5]。 2. **统一访问顺序** 确保所有事务按相同顺序访问数据: ```sql -- 反例:事务1先更新A后B,事务2先更新B后A -- 正例:所有事务统一按A->B顺序操作 ``` 避免循环等待[^2][^4]。 3. **降低隔离级别** 将隔离级别从`REPEATABLE READ`降为`READ COMMITTED`: ```sql SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; ``` 减少间隙锁(Gap Lock)的使用[^3][^5]。 #### 三、SQL与索引优化 1. **精准使用索引** - 为WHERE条件字段建立索引,避免全表扫描 - 特别关注批量操作(如`WHERE id IN (…)`),确保索引覆盖[^3]。 2. **控制锁粒度** 使用`SELECT ... FOR UPDATE SKIP LOCKED`跳过已锁记录(MySQL 8.0+): ```sql SELECT * FROM orders WHERE status='pending' FOR UPDATE SKIP LOCKED LIMIT 10; ``` 避免不必要的锁竞争[^2]。 #### 四、应用层防护 1. **重试机制** 在代码中捕获死锁异常(错误码`1213`),自动重试事务: ```java // Java示例 int retries = 3; while (retries-- > 0) { try (Connection conn = dataSource.getConnection()) { // 执行事务 break; } catch (SQLException e) { if (e.getErrorCode() == 1213) continue; else throw e; } } ``` [^2][^5] 2. **锁超时设置** 配置锁等待超时(默认50秒): ```sql SET GLOBAL innodb_lock_wait_timeout = 30; ``` 超时后自动回滚阻塞事务[^4]。 #### 五、架构级优化 1. **热点数据分离** 对高频更新数据(如计数器)采用: - 缓存中间结果(Redis) - 队列异步处理 2. **避免长事务** 监控`information_schema.innodb_trx`,强制终止长时间运行的事务[^1]。 > **关键原则**:事务尽量短小 + 访问顺序一致 + 索引精准覆盖[^3][^4][^5]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值