一、快照读

普通读(也称快照读,英文名:Consistent Read),就是单纯简单的 SELECT 语句,未加for update等,普通读的执行方式是生成 ReadView,直接利用 MVCC 机制来进行读取,基于版本的控制协议,并不会对记录进行加锁。

实现方式

快照读是通过 undo log + MVCC 来实现的,具体我们再仔细聊聊:
下图右侧黄色部分是数据:一行数据记录,主键 ID 是 10,object = ‘Goland’ ,被 update 更新为 object = ‘Python’
image.png
事务会先使用“排他锁”锁定该行,将该行当前的值复制到 undo log 中,然后再真正地修改当前行的值,最后填写事务的 DB_TRX_ID ,使用回滚指针 DB_ROLL_PTR 指向 undo log 中修改前的行。

  • DB_TRX_ID : 6 字节 DB_TRX_ID 字段,表示最后更新的事务 id ( update , delete , insert ) 。此外,删除在内部被视为更新,其中行中的特殊位被设置为将其标记为已软删除。
  • DB_ROLL_PTR : 7 字节回滚指针,指向前一个版本的 undo log 记录,组成 undo 链表。如果更新了行,则撤消日志记录包含在更新行之前重建行内容所需的信息。

注:insert undo log 只在事务回滚时需要, 事务提交就可以删掉了。update undo log 包括 update 和 delete , 回滚和快照读都需要。

Readview

Readview主要是用来做可见性判断的,即当我们去执行快照读 select 的时候,会针对我们查询的数据创建出一个 read view,来决定当前事务能看到的是哪个版本的数据,有可能是当前最新版本的数据,也可能是 undo log 中某个版本的数据,read view 遵循一个可见性算法。主要是将要修改的数据的 DB_TRX_ID 取出来,与系统其他活跃事务ID【DB_TRX_ID】做对比,如果大于或者等于这些 ID 的话,就通过 DB_ROW_PTR 指针去取出 un do log,上一层的 DB_TRX_ID 直到小于这些活跃事务 ID 为止,这样就保证了我们获取到的数据版本是当前可见的最稳定的版本。

二、当前读

当前读,读取的是最新版本,并且需要先获取对应记录的锁,如以下这些 SQL 类型:
select … lock in share mode 、select … for update、update 、delete 、insert
当前读是通过 next-key 锁(行记录锁+间隙锁)来是实现的。
这里补充下行锁的 3 种算法:

  • 行锁(Record Lock):锁直接加在索引记录上面。
  • 间隙锁(Gap Lock):是 Innodb 为了解决幻读问题时引入的锁机制,所以只有在 Read Repeatable 、Serializable 隔离级别才有。
  • Next-Key Lock :Record Lock + Gap Lock,锁定一个范围并且锁定记录本身 。