事务并发执行遇到的问题

  • 脏写(Dirty Write)
    • 如果一个事务修改了另一个未提交事务修改过的数据,那就意味着发生了脏写
  • 脏读(Dirty Read)
    • 如果一个事务读到了另一个未提交事务修改过的数据,那就意味着发生了脏读
  • 不可重复读(Non-Repeatable Read):第二次读到的数据和第一次不一样
  • 幻读(Phantom)
    • 如果一个事务先根据某些条件查询出一些记录,之后另一个事务又向表中插入了符合这些条件的记录,原先的事务再次按照该条件查询时,能把另一个事务插入的记录也读出来,那就意味着发生了幻读

SQL标准中的四种隔离级别

  • READ UNCOMMITTED:未提交读:可能发生脏读、不可重复读和幻读问题。
  • READ COMMITTED:已提交读:可能发生不可重复读和幻读问题,但是不可以发生脏读问题。
  • REPEATABLE READ:可重复读:可能发生幻读问题,但是不可以发生脏读和不可重复读的问题。
  • SERIALIZABLE:可串行化:各种问题都不可以发生。

  • MySQL的默认隔离级别为REPEATABLE READ

MVCC原理

版本链

隐藏列:

  • trx_id:每次一个事务对某条聚簇索引记录进行改动时,都会把该事务的事务id赋值给trx_id隐藏列。
  • roll_pointer:每次对某条聚簇索引记录进行改动时,都会把旧的版本写入到undo日志中,然后这个隐藏列就相当于一个指针,可以通过它来找到该记录修改前的信息。
  • 每次对记录进行改动,都会记录一条undo日志,每条undo日志也都有一个roll_pointer属性(INSERT操作对应的undo日志没有该属性,因为该记录并没有更早的版本),可以将这些undo日志都连起来,串成一个链表

image.png

  • 所有的版本都会被roll_pointer属性连接成一个链表,我们把这个链表称之为版本链,版本链的头节点就是当前记录最新的值。另外,每个版本中还包含生成该版本时对应的事务id

ReadView

  • 概述:判断一下版本链中的哪个版本是当前事务可见的
  • m_ids:表示在生成ReadView时当前系统中活跃的读写事务的事务id列表。
  • min_trx_id:表示在生成ReadView时当前系统中活跃的读写事务中最小的事务id,也就是m_ids中的最小值。
  • max_trx_id:表示生成ReadView时系统中应该分配给下一个事务的id值。
  • creator_trx_id:表示生成该ReadView的事务的事务id。

  • READ COMMITTED —— 每次读取数据前都生成一个ReadView

  • REPEATABLE READ —— 在第一次读取数据时生成一个ReadView0