死锁产生
死锁是指两个或多个事务在同一资源上相互占用, 并请求锁定对方占用的资源, 从而导致恶性循环.
当事务试图以不同的顺序锁定资源时, 就可能产生死锁. 多个事务同时锁定同一个资源时也可能会产生死锁.
锁的行为和顺序和存储引擎相关. 以同样的顺序执行语句, 有些存储引擎会产生死锁有些不会 -- 死锁有双重原因: 真正的数据冲突; 存储引擎的实现方式.
检测死锁
数据库系统实现了各种死锁检测和死锁超时的机制. InnoDB 存储引擎能检测到死锁的循环依赖并立即返回一个错误.
死锁恢复
死锁发生以后, 只有部分或完全回滚其中一个事务, 才能打破死锁, InnoDB 目前处理死锁的方法是, 将持有最少行级排他锁的事务进行回滚. 所以事务型应用程序在设计时必须考虑如何处理死锁, 多数情况下只需要重新执行因死锁回滚的事务即可.
外部锁的死锁检测
发生死锁后, InnoDB 一般都能自动检测到, 并使一个事务释放锁并回退, 另一个事务获得锁, 继续完成事务. 但在涉及外部锁, 或涉及表锁的情况下, InnoDB 并不能完全自动检测到死锁, 这需要通过设置锁等待超时参数 innodb_lock_wait_timeout 来解决
死锁影响性能
死锁会影响性能而不是会产生严重错误, 因为 InnoDB 会自动检测死锁状况并回滚其中一个受影响的事务. 在高并发系统上, 当许多线程等待同一个锁时, 死锁检测可能导致速度变慢. 有时当发生死锁时, 禁用死锁检测 (使用 innodb_deadlock_detect 配置选项) 可能会更有效, 这时可以依赖 innodb_lock_wait_timeout 设置进行事务回滚.
MyISAM 避免死锁
在自动加锁的情况下, MyISAM 总是一次获得 SQL 语句所需要的全部锁, 所以 MyISAM 表不会出现死锁.
InnoDB 避免死锁
为了在单个 InnoDB 表上执行多个并发写入操作时避免死锁, 可以在事务开始时通过为预期要修改的每个元祖 (行) 使用 SELECT ... FOR UPDATE 语句来获取必要的锁, 即使这些行的更改语句是在之后才执行的.
在事务中, 如果要更新记录, 应该直接申请足够级别的锁, 即排他锁, 而不应先申请共享锁, 更新时再申请排他锁, 因为这时候当用户再申请排他锁时, 其他事务可能又已经获得了相同记录的共享锁, 从而造成锁冲突, 甚至死锁
如果事务需要修改或锁定多个表, 则应在每个事务中以相同的顺序使用加锁语句. 在应用中, 如果不同的程序会并发存取多个表, 应尽量约定以相同的顺序来访问表, 这样可以大大降低产生死锁的机会
通过 SELECT ... LOCK IN SHARE MODE 获取行的读锁后, 如果当前事务再需要对该记录进行更新操作, 则很有可能造成死锁.
改变事务隔离级别
如果出现死锁, 可以用 SHOW INNODB STATUS 命令来确定最后一个死锁产生的原因. 返回结果中包括死锁相关事务的详细信息, 如引发死锁的 SQL 语句, 事务已经获得的锁, 正在等待什么锁, 以及被回滚的事务等. 据此可以分析死锁产生的原因和改进措施.
来源: http://www.bubuko.com/infodetail-2946401.html