行锁:InnoDB替代MyISAM的重要原因
InnoDB能够替代MyISAM的原因有二:
- InnoDB支持事务:适合在并发条件下要求数据一致的场景
- InnoDB支持行锁:有效降低由于删除或者更新导致的锁定
1. 两阶段锁
传统的关系型数据库加锁的一个原则是:两阶段锁原则。
两阶段锁:锁操作分为两个阶段,加锁阶段和解锁阶段,并且保证加锁阶段和解锁阶段不相交。
我们可以通过下面这张表理解两阶段锁:
2. InnoDB行锁模式
InnoDB实现了以下两种类型的行锁:
- 共享锁:允许一个事务去读一行,阻止其他事务获得相同数据集的排它锁
- 排它锁:允许获得排它锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和排它写锁
对于普通select语句,InnoDB不会加任何锁,事务可以通过以下语句显式给记录集加共享锁或排它锁:
- 共享锁:select * from table where … lock in share mode
- 排它锁:select * from table where … for update
3. InnoDB行锁算法
InnoDB行锁的三种算法:
- Record Lock:单个记录上的索引加锁
- Gap Lock:间隙锁,对索引项之间的间隙加锁,但不包括记录本身
- Next-Key Lock: Gap Lock + Record Lock, 锁定一个范围,并且锁定记录本身
InnoDB行锁实现特点意味着:如果不通过索引条件检索数据,那么InnoDB将对表中所有记录加锁,实际效果跟表锁一样。
4. 事务隔离级别
不同事务隔离级别对应的行锁也是不一样的,MySQL有4中事务隔离级别:
- 读未提交:所有事务都可以看到其他未提交事务的执行结果,可能会出现脏读
- 读已提交:一个事务只能看见已提交事务所做的改变。因为同一事务的其他实例在该实例处理期间可能会有新的提交,所以可能会出现幻读
- 可重复读:MySQL的默认隔离级别,确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。消除了脏读、不可重复读,可能会出现幻读
- 串行:最高的隔离级别,通过强制事务排序,使之不可能出现幻读
脏读:读取到未提交的数据
幻读:一个事务按照相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的数据或者发现以前检索过的数据发生了修改
5. 读已提交隔离级别下的行锁
- 没有索引的情况下,InnoDB的当前读(select … for update)会对所有记录都加锁
- 在查询条件有索引的情况下,那么SQL需要在满足条件的索引上加锁,并且会在他们对应的聚簇索引上加锁
- 在更新数据时,如果条件字段没索引,则表中所有记录都会被加上排它锁,所以尽量让查询走索引
6.可重复读隔离级别下的行锁
- 条件字段非索引的当前读不但会把每条记录都机上排它锁,还会把每个间隙加上间隙锁
- 唯一索引为条件的当前读不会有间隙锁,因为根据唯一索引查询最多就一条记录,而且相同索引记录的值,一定不会再新增
- 可重复读通过间隙锁解决了幻读