Mysql:MySQL死锁的问题

死锁产生

死锁是指两个或多个事务在同一资源上相互占用,并请求锁定对方占用的资源,从而导致恶性循环。

当事务试图以不同的顺序锁定资源时,就可能产生死锁。多个事务同时锁定同一个资源时也可能会产生死锁。

锁的行为和顺序和存储引擎相关。以同样的顺序执行语句,有些存储引擎会产生死锁有些不会——死锁有双重原因:真正的数据冲突;存储引擎的实现方式。

检测死锁

数据库系统实现了各种死锁检测和死锁超时的机制。InnoDB存储引擎能检测到死锁的循环依赖并立即返回一个错误。

死锁恢复

死锁发生以后,只有部分或完全回滚其中一个事务,才能打破死锁,InnoDB目前处理死锁的方法是,将持有最少行级排他锁的事务进行回滚。所以事务型应用程序在设计时必须考虑如何处理死锁,多数情况下只需要重新执行因死锁回滚的事务即可。

死锁影响性能

死锁会影响性能而不是会产生严重错误,因为InnoDB会自动检测死锁状况并回滚其中一个受影响的事务。
在高并发系统上,当许多线程等待同一个锁时,死锁检测可能导致速度变慢。
有时当发生死锁时,禁用死锁检测(使用innodb_deadlock_detect配置选项)可能会更有效,这时可以依赖innodb_lock_wait_timeout设置进行事务回滚。

InnoDB避免死锁

为了在单个InnoDB表上执行多个并发写入操作时避免死锁,可以在事务开始时通过为预期要修改的每个元祖(行)使用SELECT … FOR UPDATE语句来获取必要的锁,即使这些行的更改语句是在之后才执行的。

在事务中,如果要更新记录,应该直接申请足够级别的锁,即排他锁,而不应先申请共享锁、更新时再申请排他锁,因为这时候当用户再申请排他锁时,其他事务可能又已经获得了相同记录的共享锁,从而造成锁冲突,甚至死锁。

如果事务需要修改或锁定多个表,则应在每个事务中以相同的顺序使用加锁语句。在应用中,如果不同的程序会并发存取多个表,应尽量约定以相同的顺序来访问表,这样可以大大降低产生死锁的机会。

通过SELECT … LOCK IN SHARE MODE获取行的读锁后,如果当前事务再需要对该记录进行更新操作,则很有可能造成死锁。

解决的办法当然是进行数据库的事务回滚

try:
    add_comment = Comment(theme=theme, email=email, content=content, uuid=uuid)
    db.session.add(add_comment)
    db.session.commit()
    return "评论成功"
except Exception as e:
    db.session.rollback()
    return           


确定死锁产生的原因

如果出现死锁,可以用 SHOW INNODB STATUS 命令来确定最后一个死锁产生的原因。返回结果中包括死锁相关事务的详细信息,如引发死锁的 SQL 语句,事务已经获得的锁,正在等待什么锁,以及被回滚的事务等。据此可以分析死锁产生的原因和改进措施。

mysql 死锁解决
查看锁记录等待时间
SHOW VARIABLES LIKE 'innodb_lock_wait_timeout';

超时等待时间修改为5秒: SET innodb_lock_wait_timeout=5;

查看锁住的进程 杀死它
SELECT * FROM information_schema.INNODB_TRX;

trx_rows_locked: 事务锁定行数
trx_rows_modified: 事务修改行数

#首先查询是否锁表
SHOW OPEN TABLES WHERE In_use > 0;

#查询进程,保证拥有超级管理员权限
SHOW PROCESSLIST;

#杀死进程
KILL 4503;

#查看正在锁的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;

#查看等待锁的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;

目前最暴力的方法就是直接 KILL 掉 等待死锁的进程


本文参考:Mysql 死锁解决MySQL死锁


MYSQL

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!