msyql的事务隔离级别如下:
- Read uncommitted(未提交读)
事务中的修改,即使没有提交,对其它事务也是可见的. 脏读(Dirty Read).
- Read committed(提交读)
一个事务开始时,只能"看见"已经提交的事务所做的修改. 这个级别有时候也叫
- Repeatable read(可重复读) 这是myslq默认的隔离级别
- Serializable(串行化)
强制事务串行执行,避免了上面说到的 脏读,不可重复读,幻读 三个的问题.
mysql默认的隔离级别其实是没有解决可重复读和幻读的问题,但是它还是给解决了。
mvcc多版本并发控制(Multi-Version Concurrency Control)
InnoDB的MVCC是通过在每行记录后面保存2个隐藏的列来实现的,一列保存了行的创建时间,一列保存了行的过期时间(或删除时间),但它们实际都存储的是系统版本号。
mysql的mvcc只在 Read Committed和 Repeatable read两个隔离级别下工作。
在MVCC机制下,mysql别下的增删改查变成如下模式。
读模式:
- InnoDB只查找版本早于当前事务版本的数据行(行的系统版本号小于等于事务的系统版本号)
- 行的删除号要么未定义,要么大于当前事务版本号,这样可以确保事务读取到的行,在事务开始之前未被删除.
写模式:
- INSERT,InnoDB 为新插入的每一行保存当前系统版本号做为行版本号。
- DELETE,INNODB 为删除的每一行保存当前系统版本号作为行删除标识。
- UPDATE,InnoDB 为插入的每一行新记录,保存当前系统版本号作为行版本号,同时保存当前系统版本号到原来的行作为行删除标识.
一致性非锁定读,是InnoDB存储引擎下的读取数据的方式( read committed 和 repeatable read).
一致性非锁定读,我的理解是它的读取方式是把: 事务隔离级别,MVCC,InnoDB锁结合起来运用到实现 Mysql读的一种方式.
- 在read committed隔离级别下:
一致性非锁定读总是读取被锁定行的最新一份(最新的一行)快照数据. 产生了不可重复读的问题.
- 在repeatable read 事务隔离级别下:
一致性非锁定读总是读取事务开始时(第一行)的行数据版本. 解决不可重复读的问题
一致性锁定读
- select .... for update. 加X锁(排它锁)
- select .... lock in share mode. 加S锁(共享锁)
InnoDB 锁的算法
- Record Lock: 单个行记录的锁
- GAP Lock: 间隙锁,锁定一个范围,但不包含记录本身
间隙锁,它会锁住两个索引之间的区域。比如select * from user where id>1 and id<10 for update,就会在id为(1,10)的索引区间上加Gap Lock。
- Next-Key Lock: Gap Lock+Record Lock 锁定一个范围并锁定记录本身
也叫间隙锁,它是Record Lock + Gap Lock形成的一个闭区间锁。比如select * from user where id>=1 and id<=10 for update,就会在id为[1,10]的索引闭区间上加Next-Key Lock
innerDB默认的事务隔离级别(repeatable read),采用的是Next-Key Locking的方式来加锁。
read committed隔离级别下采用的是: Record Lock 的方式来加锁。
Next-key lock的具体实现:
默认存储引擎下, 比如表A 上的id字段有索引, 并且id有 3,8,12,20这几个值,那么该索引可能被Next-key locking区间为:
负无穷,3)[3,8)[8,12),[12,20),[20,正无穷),当事务T1锁定了 [8,12),[12,20)这2个区间时,当插入15时,上面的区间变成:[8,12),[12,15),[15,20)。
此时如果执行如下语句:select * from A where id>16 for update. InnoDB会对(16,正无穷) 加锁,但在 read committed的事务隔离级别下,因为采用Record Lock,只会锁定20这个值.
如果在此时另外一个事务T2,插入了22这个值,此时, read committed 隔离级别下就会产生"幻读"的问题.
但在InnoDB默认存储引擎下的Next-key Lock 模式下,22是插入是会被阻塞的,直到事务T1提交后,释放X锁,才能提交22这值.这样,InnoDB就这样解决了幻读的问题.
总结:
- InnoDB用MVCC来实现非阻塞的读操作,不同隔离级别下,MVCC通过读取不同版本的数据来解决"不可重复读" 的问题
- InnoDB的默认隔离级别采用Next-key Lock(间隙锁) 来解决幻读问题. 而 read committed隔离级别采用Record锁,因此会产生"幻读"问题.
本文主要参考:https://blog.csdn.net/tangkund3218/article/details/47704527