为了阻止幻读情况的发生, InnoDB使用了一种方法next-key锁将索引行锁和间隔锁合并在一起。 InnoDb会在索引扫描的行上施加行级共享锁或者排他锁,而next-key锁也会在每个索引行之前的间隔上施加锁,会导致其他的session不能在每个索引之前的间隔内插入新的索引值
间隔锁会施加在索引读碰到的行数据上,所以对上例来说为了阻止插入任何>100的值,也会将最后扫描的索引值102之前的间隔锁住
InnoDB锁性能监控
1 2 3 4 5 6 7 8 9 10 11
mysql> show status like '%innodb_row_lock%'; +-------------------------------+-------+ | Variable_name | Value | +-------------------------------+-------+ | Innodb_row_lock_current_waits | 0 | | Innodb_row_lock_time | 0 | | Innodb_row_lock_time_avg | 0 | | Innodb_row_lock_time_max | 0 | | Innodb_row_lock_waits | 0 | +-------------------------------+-------+ 5 rows in set (0.00 sec)
Innodb_row_lock_current_waits:当前等待锁的数量
Innodb_row_lock_time:系统启动到现在、锁定的总时间长度
Innodb_row_lock_time_avg:每次平均锁定的时间
Innodb_row_lock_time_max:最长一次锁定时间
Innodb_row_lock_waits:系统启动到现在、总共锁定次数
死锁的情况发生在不同的的事务相互之间拥有对方需要的锁,而导致相互一直无限等待
死锁可能发生在不同的事务都会对多个相同的表和相同的行上施加锁,但事务对表的操作顺序不相同
为了减少死锁的发生,要避免使用lock table语句,要尽量让修改数据的范围尽可能的小和快速;当不同的事务要修改多个表或者大量数据时,尽可能的保证修改的顺序在事务之间要一致
默认情况下InnoDB下的死锁自动侦测功能是开启的,当InnoDB发现死锁时,会将其中的一个事务作为牺牲品回滚。
show variables like ‘innodb_deadlock_detect’;
通过innodb_lock_wait_timeout参数配置自动侦测功能是否开启,如果关闭的话, InnoDB就会使用innodb_lock_wait_timeout参数来自动回滚等待足够时间的事
mysql> show variables like '%innodb_lock_wait_timeout%';
可以通过show engine innodb status语句查看最后一次发生死锁的情况
比如以下例子产生的死锁:
1 2 3 4 5
事务1:update temp set name=‘aa’ where > 事务2:update temp set name=‘bb’ where > 事务1:update temp set name=‘aaa’ where > 事务2: update temp set name=‘bbb’ where > ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
InnoDB死锁检测和回滚
默认情况下死锁检测功能是开启的,当死锁发生时InnoDB会自动检测
到并牺牲(回滚)其中的一个或者几个事务,以便让其他的事务继续执行
下去。
InnoDB选择牺牲的事务往往是代价比较小的事务,其代价计算是根据
事务insert,update, delete的数据行规模决定
如果事务中的某个语句因为错误而回滚,则这个语句上的锁可能还会
保留,是因为InnoDB仅会存储行锁信息,而不会存储行锁是由事务中
的哪个语句产生的
如果在一个事务中, select语句调用了函数,而函数中的某个语句执行
失败,则那个语句会回滚,如果在整个事务结束时执行rollback,则整
个事务回滚
可以通过innodb_deadlock_detect 参数关闭死锁检测功能,而仅仅用
innodb_lock_wait_timeout的功能来释放锁等待
在事务性数据库中,死锁是个经典的问题,但只要发生的频率不高则死锁问题不需要太过担心