SQL标准定义了4类隔离级别:
1 Read Uncommitted(读未提交)。缺点:脏读(一个事务读取到另一事务未提交的数据)
2 Read Committed(读提交)。解决了脏读问题, 缺点:不可重复读(同一个事务里面读取到其他事务commit的数据,主要是针对于update)。
3 Repeatable Read(可重复重读)。解决了不可重复读,缺点:幻读(同一个事务里面读取到其他事务commit的数据,主要是针对于insert)。
4 Serializable(可串行化)。解决了不可重复读,缺点:每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。
PS : 需要区分:不可重复读(update)和幻读(insert|delete)。
幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。
Innodb默认隔离级别是可重复读(RR) :
1) 使用MVCC(多版本并发控制)来解决不可重复读问题,
2) 使用间隙锁(Gap锁)来解决幻读。
所以说Innodb存储引擎在默认隔离的隔离级别下已经能完全保证事物的隔离性要求,即达到SQL标准的Serializable隔离级别。
PS : Innodb存储引擎支持XA事务,通过XA事务可以来支持分布式事务的实现,需要将隔离级别设置为Serializable。在Serializable这个级别下,select语句也会加上共享锁(不要看到select就说不会加锁了)。
一致性的非锁定读
MySQL为了减少锁处理(包括等待其它锁)的时间,提升并发能力,引入了快照读的概念(即一致性的非锁定读)。
1 Innodb通过多版本并发控制(MVCC)技术来读取当前时间数据库中行的数据。如果读取的正在执行Update,Delete,这时读取操作不会因此而会等待行上锁的释放,相反Innodb会去读取行的一个快照数据。
2 之所以叫非锁定读,因为不需要等待访问的行上X锁的释放。快照的数据是指该行之前版本的数据,通过Undo段(主要用于事务的回滚)来实现。
3 只有Read Committed和Repeatable Read 两种事务隔离级别才能使用MVCC(Read Uncommited由于是读到未提交的,所以不存在版本的问题,而Serializable 则会对所有读取的行加锁),但是它们对于快照数据的定义是不相同的。
RC:总是读取被锁定行的最新一份快照数据;
RR:总是读取事务开始时的行数据版本。
因此在RC隔离级别下,会产生不可重复读的问题。因为它总是读取的最新的快照。
Innodb锁算法
Innodb有三种行锁算法:
1) Record Lock:单个行记录锁
2 )Gap Lock:间隙锁,锁定一个范围,但不包括本身
3)Next-Key Lock:Gap Lock + Record Lock ,锁定一个范围,并且包括本身
默认情况下,InnoDB工作在可重复读隔离级别下,并且会以Next-Key Lock的方式对数据行进行加锁,这样可以有效防止幻读的发生。Next-Key Lock是行锁和间隙锁的组合,当InnoDB扫描索引记录的时候,会首先对索引记录加上行锁(Record Lock),再对索引记录两边的间隙加上间隙锁(Gap Lock)。加上间隙锁之后,其他事务就不能在这个间隙修改或者插入记录。
锁选择
例子: update from t1 set v2=0 where v1=5
1)如果v1是唯一索引(包括主键索引),使用Record Lock
2)如果v1没有建立索引,此时会进行全表扫描,扫表的时候,要阻止其他任何的更新操作,所以上升为表锁
3)如果v1是二级索引,使用Next-Key Lock