mysql 一次死锁的处理

本文通过分析并发操作引发的MySQL死锁现象,介绍了如何通过调整SQL语句顺序和避免多表更新来解决死锁问题。

昨天用apache bench压操作的时候发生严重的死锁。

查了一下操作:

操作1,insert into tableA 然后 用tableA 的数据 update tableB 

操作2,delete from tableA 然后 用tableA 的数据 update tableB 

用了很小的并发,去执行 操作1后立即执行操作2。发现只有两条成功,后面的都在等待锁 。

异常:mysql 'Deadlock found when trying to get lock; try restarting transaction'

从操作上看,两个操作的顺序是对的,不应该发生死锁。

然后看了sql

操作2:

delete from tableB where SId = 987


update tableA a
        JOIN
    (select 
        '123456' AS CId,
            count(c.SId) as Count1,
            IFNULL(SUM(CASE
                WHEN s.dType = 'a' THEN 1
                ELSE 0
            END), 0) as Count2,
            IFNULL(SUM(CASE
                WHEN s.dType = 'b' THEN 1
                ELSE 0
            END), 0) as Count3
    from
        tableB c
    left JOIN tableC s ON c.CId = s.Id
    where
        c.CId = '123456') as co ON a.Id = co.CId 
set 
    a.Count1 = co.Count1,
    a.Count2 = co.Count2,
    a.Count3 = co.Count3

然后分析认为后面的sql 在更新tableA时,可能导致了tableB被锁,因此导致死锁。

然后把,后面那个sql的查询的部分拆出来,然后用查询的结果去更新tableA。

拆出来后发现没有了死锁。


总结:

1,确保sql的顺序一致,都是先更新tableB, 再更新tableA

2,  尽量少写multi table update,这些会锁住多个表的sql。

### 解决MySQL死锁问题的方法 #### 使用`SHOW ENGINE INNODB STATUS` 为了诊断死锁,可以利用命令 `SHOW ENGINE INNODB STATUS\G;` 来获取最近一次发生的死锁信息。这有助于理解哪些查询参与到了死锁之中以及涉及的具体表和索引[^1]。 ```sql SHOW ENGINE INNODB STATUS\G; ``` 此命令返回的信息非常详尽,包含了发生死锁的时间戳、事务ID、锁定的记录以及其他相关细节。 #### 设置合理的隔离级别 调整事务的隔离级别可以在一定程度上减少死锁的发生概率。较低级别的隔离(如读已提交)可能降低死锁的风险,但这取决于应用程序的需求和数据一致性要求[^2]。 设置会话或全局范围内的隔离级别: ```sql SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; -- 或者对于整个服务器生效 SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED; ``` #### 控制事务大小并尽早释放锁 保持事务尽可能短小,并尽快完成操作以释放所持有的任何锁。长时间运行的大事务增加了与其他事务冲突的可能性,从而提高了死锁的概率[^4]。 启动一个新的事务时应考虑如下方式: ```sql START TRANSACTION; -- 执行必要的 SQL 语句... COMMIT; ``` #### 尝试捕获异常重试逻辑 由于无法完全预防死锁,在应用层面上实现一个简单的重试机制是一个有效的策略。当检测到错误码为1213(即发生了死锁),可以选择回滚当前事务并重新尝试执行相同的SQL序列。 Python伪代码示例展示如何处理这种情况: ```python import pymysql def execute_with_retry(connection, sql_query, max_retries=3): retries = 0 while True: try: with connection.cursor() as cursor: cursor.execute(sql_query) connection.commit() break except pymysql.MySQLError as e: if e.args[0] == 1213 and retries < max_retries: retries += 1 continue raise execute_with_retry(conn, "YOUR_SQL_QUERY_HERE") ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值