一文彻底读懂MySQL事务的四大隔离级别( 三 )


  • 读写锁
  • 一致性快照读,即 MVCC
MySql使用不同的锁策略(Locking Strategy)/MVCC来实现四种不同的隔离级别 。RR、RC的实现原理跟MVCC有关,RU和Serializable跟锁有关 。
读未提交(Read Uncommitted)官方说法:
SELECT statements are performed in a nonlocking fashion, but a possible earlier version of a row might be used. Thus, using this isolation level, such reads are not consistent.
读未提交,采取的是读不加锁原理 。
  • 事务读不加锁,不阻塞其他事务的读和写
  • 事务写阻塞其他事务写,但不阻塞其他事务读;
串行化(Serializable)官方的说法:
InnoDB implicitly converts all plain SELECT statements to SELECT ... FOR SHARE if autocommit is disabled. If autocommit is enabled, the SELECT is its own transaction. It therefore is known to be read only and can be serialized if performed as a consistent (nonlocking) read and need not block for other transactions. (To force a plain SELECT to block if other transactions have modified the selected rows, disable autocommit.)
  • 所有SELECT语句会隐式转化为SELECT ... FOR SHARE,即加共享锁 。
  • 读加共享锁,写加排他锁,读写互斥 。如果有未提交的事务正在修改某些行,所有select这些行的语句都会阻塞 。
MVCC的实现原理MVCC,中文叫多版本并发控制,它是通过读取历史版本的数据,来降低并发事务冲突,从而提高并发性能的一种机制 。它的实现依赖于隐式字段、undo日志、快照读&当前读、Read View,因此,我们先来了解这几个知识点 。
隐式字段对于InnoDB存储引擎,每一行记录都有两个隐藏列DB_TRX_ID、DB_ROLL_PTR,如果表中没有主键和非NULL唯一键时,则还会有第三个隐藏的主键列DB_ROW_ID 。
  • DB_TRX_ID,记录每一行最近一次修改(修改/更新)它的事务ID,大小为6字节;
  • DB_ROLL_PTR,这个隐藏列就相当于一个指针,指向回滚段的undo日志,大小为7字节;
  • DB_ROW_ID,单调递增的行ID,大小为6字节;
 
一文彻底读懂MySQL事务的四大隔离级别

文章插图
 
undo日志
事务未提交的时候,修改数据的镜像(修改前的旧版本),存到undo日志里 。以便事务回滚时,恢复旧版本数据,撤销未提交事务数据对数据库的影响 。undo日志是逻辑日志 。可以这样认为,当delete一条记录时,undo log中会记录一条对应的insert记录,当update一条记录时,它记录一条对应相反的update记录 。存储undo日志的地方,就是回滚段 。
多个事务并行操作某一行数据时,不同事务对该行数据的修改会产生多个版本,然后通过回滚指针(DB_ROLL_PTR)连一条Undo日志链 。
我们通过例子来看一下~
mysql> select * from account ;+----+------+---------+| id | name | balance |+----+------+---------+|1 | Jay|100 |+----+------+---------+1 row in set (0.00 sec)复制代码
  • 假设表accout现在只有一条记录,插入该该记录的事务Id为100
  • 如果事务B(事务Id为200),对id=1的该行记录进行更新,把balance值修改为90
事务B修改后,形成的Undo Log链如下:
一文彻底读懂MySQL事务的四大隔离级别

文章插图
 
快照读&当前读快照读:
读取的是记录数据的可见版本(有旧的版本),不加锁,普通的select语句都是快照读,如:
select * from account where id>2;复制代码当前读:
读取的是记录数据的最新版本,显示加锁的都是当前读
select * from account where id>2 lock in share mode;select * fromaccount where id>2 for update;复制代码Read View
  • Read View就是事务执行快照读时,产生的读视图 。
  • 事务执行快照读时,会生成数据库系统当前的一个快照,记录当前系统中还有哪些活跃的读写事务,把它们放到一个列表里 。
  • Read View主要是用来做可见性判断的,即判断当前事务可见哪个版本的数据~
为了下面方便讨论Read View可见性规则,先定义几个变量
m_ids:当前系统中那些活跃的读写事务ID,它数据结构为一个List 。min_limit_id:m_ids事务列表中,最小的事务ID max_limit_id:m_ids事务列表中,最大的事务ID