知识点整理(九)——mysql的MVCC

mysql事务控制

并发事务

并发事务会带来一些问题

  • 脏读
    一个事务读取到了另一个事务修改但未提交的数据。

  • 不可重复读
    一个事务中多次读取同一行记录不一致。

  • 幻读
    一个事务中多次按相同条件查询,结果不一致。(多了或者少了)

事务隔离级别

事务隔离级别 脏读 不可重复读 幻读
读未提交 会发生 会发生 会发生
读已提交 × 会发生 会发生
可重复读 × × 会发生
串行化 × × ×

注意mysql的MVCC解决了部分幻读问题(不能解决当前读下的幻读)。

事务并发问题主要是解决读读、读写、写写之间的问题。加排它锁性能太低。使用读写锁能解决读读之间性能问题,但是无法解决读写之间的性能。

MVCC

MVCC使用了Copy On Write的思想。可以支持读读并行,也可以支持读写并行。写写依旧无法并行。

概念

MVCC(Multi Version Concurrency Control)被称为多版本控制。在数据库中为了实现高并发数据访问,对数据进行多版本处理,并通过事务的可见性来保证事务能看到自己应该看到的数据版本。

MVCC最大的好处是读不加锁,读写不冲突,这对读多写少的系统非常重要。现阶段几乎所有关系型数据库都支持MVCC。mysql的MVCC只在读已提交和可重复读2种隔离级别下工作。

实现原理

  • 快照读:读取快照版本,普通的select查询,不加锁。
  • 当前读:读取记录的最新版本,会加锁。(select for update / lock in share mode/insert/update/delete)
  • 事务id:mysql会维护一个全局事务id,每个事务都不一样,自增。

当我们向mysql插入一条记录时,mysql会这么存储。(假设事务1)

{cat_tips_info color=””}
DATA_ROW_ID:隐藏数据ID
DB_TRX_ID:当前事务版本号
DB_ROLL_PT:回滚指针
{/cat_tips_info}

当我们修改这条记录后,mysql会这么存储。(假设事务10)

再修改一下会变成这样。(假设事务15)

mysql会把修改前的记录复制到undo log中,然后修改现有数据,设置当前事务id,设置回滚指针指向之前的记录行。

具体如何实现可重复读呢?

在事务开启时创建readView,之后只按照readView内容进行读取。这样就保证了可以重复读到相同快照内容。(注意事务开启时怎么理解?begin/start transaction时第一个快照读语句,而要立刻开启是start transaction with consistent snapshot)

readView里有哪些东西呢?

  • m_ids,当前有哪些事务正在执行,且还没有提交,这些事务的 id 就会存在这里;
  • min_trx_id,是指 m_ids 里最小的值;
  • max_trx_id,是指m_ids 里最大的值;
  • creator_trx_id,每开启一个事务都会生成一个 ReadView,而 creator_trx_id 就是这个开启的事务的 id。
  1. 如果查到一条记录,其中的DB_TRX_ID小于min_trx_id,那么肯定可以看到这条数据。因为当前事务肯定在活动中,肯定大于min_trx_id,那么本事务开启时这个数据已经提交了。
  2. 如果查到一条记录,其中的DB_TRX_ID大于max_trx_id,那么这条数据肯定看不到。因为当前事务开启的时候记录所在的事务还没开启。这个时候应该通过回滚指针找到之前版本的记录进行判断。
  3. 如果查到一条记录,其中的DB_TRX_ID介于min_trx_id和max_trx_id之间,那就看是否在m_ids内,如果在,就表示本事务开始时,记录所在事务还没提交,所以不能看到,反之则能看到。