MYSQL死锁

本文详细解释了数据库中InnoDB存储引擎下死锁的发生原因、检测方法及避免策略,包括死锁的四个必要条件和常见死锁类型。

声明:以下讨论只是针对InnoDB存储引擎。

何为死锁?
 
  死锁是对资源的分配和使用不当而造成的。是两个进程争夺某一资源而出现相互等待的现象。
  具体的来讲,出现死锁需要满足四个必要条件:
(1)互斥条件:每一个资源都只能被一个进程使用
(2)请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放
(3)不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4)循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
   很显然,出现死锁需要两个或者两个以上的进程,换句话说,死锁发生在并发的程序中。
   在Mysql中,由于目前只有InnoDB引擎使用事务(InnoDB支持锁),便有了InnoDB和死锁的旷世基情。

死锁的检测

   1、通过使用Show innodb status检查引擎状态 ,可以看到哪些语句产生deadlock
   2、MySQL提供了一个information_schema,通过查看innodb_locksinnodb_trxinnodb_lock_waits这几个表检测死锁。

    因循环等待条件而产生的死锁只有可能是四种形式:

两张表两行记录交叉申请互斥锁、同一张表则存在主键索引锁冲突、

主键索引锁与非聚簇索引锁冲突、锁升级导致的锁等待队列阻塞。


死锁避免


1、如果使用insert…select语句备份表格且数据量较大,在单独的时间点操作,避免与其他sql语句争夺资源,或使用      select into outfile加上load data infile代替 insert…select,这样不仅快,而且不会要求锁定
2、 一个锁定记录集的事务,其操作结果集应尽量简短,以免一次占用太多资源,与其他事务处理的记录冲突。
3、更新或者删除表格数据,sql语句的where条件都是主键或都是索引,避免两种情况交叉,造成死锁。对于where      子句较复杂的情况,将其单独通过sql得到后,再在更新语句中使用。
4.、sql语句的嵌套表格不要太多,能拆分就拆分,避免占有资源同时等待资源,导致与其他事务冲突。
5.、对定点运行脚本的情况,避免在同一时间点运行多个对同一表进行读写的脚本,特别注意加锁且操作数据量比较       大的语句。
6、应用程序中增加对死锁的判断,如果事务意外结束,重新运行该事务,减少对功能的影响。

本文转载自:http://www.cnblogs.com/benshan/archive/2013/05/09/3068886.html


### 解决 MySQL 死锁问题的方法 #### 1. **捕获并重试死锁异常** 当发生死锁时,MySQL 会自动回滚其中个事务以解除冲突。因此,在应用程序层面可以设计逻辑来捕获 `Deadlock` 异常,并重新执行失败的事务[^1]。 ```python import pymysql def execute_transaction(): try: connection = pymysql.connect(host='localhost', user='root', password='password', database='test') cursor = connection.cursor() # 开始事务 cursor.execute("START TRANSACTION;") # 更新操作可能导致死锁 cursor.execute("UPDATE table_name SET column_name = 'value' WHERE id = 1;") # 提交事务 connection.commit() except pymysql.err.OperationalError as e: if "deadlock" in str(e).lower(): # 捕获死锁错误 print("Detected deadlock, retrying...") execute_transaction() # 重试事务 finally: if connection.open: connection.close() execute_transaction() ``` --- #### 2. **优化查询索引结构** 死锁通常发生在并发更新同数据集的情况下。通过改进查询性能调整索引策略,可以减少锁定范围,从而降低死锁发生的概率[^3]。 - 使用更细粒度的锁机制(如行级锁而非页级锁)。 - 确保涉及多张表的操作遵循致的访问顺序,避免循环依赖。 例如,如果两个事务分别按不同顺序访问两张表,则容易引发死锁: ```sql -- 事务 A BEGIN; SELECT * FROM orders WHERE order_id = 1 FOR UPDATE; SELECT * FROM customers WHERE customer_id = 1 FOR UPDATE; -- 事务 B BEGIN; SELECT * FROM customers WHERE customer_id = 1 FOR UPDATE; SELECT * FROM orders WHERE order_id = 1 FOR UPDATE; COMMIT; ``` 上述场景可以通过统访问顺序解决死锁风险。 --- #### 3. **实施读写分离架构** 对于高并发环境下的数据库压力,建议采用读写分离技术。将写入请求集中于主节点,而读取请求分发至多个从节点进行负载均衡[^4]。这样不仅可以提升整体吞吐量,还能有效缓解因频繁修改相同记录而导致的竞争状况。 配置 Proxysql 或 MaxScale 工具实自动化路由管理是个可行的选择[^2]: ```bash # 添加 proxysql 用户,默认指向写组 insert into mysql_users (username,password,default_hostgroup) values ('proxysql','xxxx',2); load mysql users to runtime; save mysql users to disk; ``` --- #### 4. **定期监控与分析死锁日志** 启用 InnoDB 的扩展状态报告功能 (`SHOW ENGINE INNODB STATUS`) ,定位具体哪些 SQL 语句触发了死锁事件[^5]。基于这些信息进步调优业务流程或者重构相关模块代码。 --- ### 总结 综合运用以上方法能够显著改善系统的稳定性响应速度。然而需要注意的是,完全杜绝死锁象几乎是不可能的任务;重点在于如何快速恢复以及最小化其影响程度。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值