最新文章专题视频专题问答1问答10问答100问答1000问答2000关键字专题1关键字专题50关键字专题500关键字专题1500TAG最新视频文章推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37视频文章20视频文章30视频文章40视频文章50视频文章60 视频文章70视频文章80视频文章90视频文章100视频文章120视频文章140 视频2关键字专题关键字专题tag2tag3文章专题文章专题2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章专题3
当前位置: 首页 - 科技 - 知识百科 - 正文

我所理解的MySQL之四:事务、隔离级别及MVCC

来源:动视网 责编:小采 时间:2020-11-08 22:13:17
文档

我所理解的MySQL之四:事务、隔离级别及MVCC

我所理解的MySQL之四:事务、隔离级别及MVCC:.markdown-body{word-break:break-word;line-height:1.75;font-weight:400;font-size:15px;overflow-x:hidden;color:#333}.markdown-body h1.markdown-body h2.markdown-body h3.markdown-body h4.markdown-body h5.markdown-body h6{line-height:1.5;ma
推荐度:
导读我所理解的MySQL之四:事务、隔离级别及MVCC:.markdown-body{word-break:break-word;line-height:1.75;font-weight:400;font-size:15px;overflow-x:hidden;color:#333}.markdown-body h1.markdown-body h2.markdown-body h3.markdown-body h4.markdown-body h5.markdown-body h6{line-height:1.5;ma


幻读会发生在读未提交、读已提交、可重复读的隔离级别中。

这里需要额外注意的是:幻读和不可重复读都是说在一个事务中的同一个查询语句结果不同,但幻读更侧重于查询到其他事务新插入的数据(insert)或其他事务删除的数据(delete),而不可重复读的范围更广,只要结果不同就可以认为是不可重复读,但一般我们认为不可重复读更侧重于其他事务对数据的更新(update)。

4.4 小结

通过上面的描述,我们已经知道四种隔离级别的概念以及它们分别会遇到的问题,事务的隔离级别越高,隔离性就越强,所遇到的问题也就越少。但同时,隔离级别越高,并发能力就越弱。

下表是对隔离级别的概念不同隔离级别会发生的问题情况的小结:

隔离级别脏读不可重复读幻读概念
读已提交事务中的修改,即便没有提交,对其他事务也都是可见的
读未提交
事务中的修改只有在提交之后,才会对其他事务可见
可重复读

一个事务中多次查询相同的记录,结果总是一致的
可串行化


事务都是串行执行的,读会加读锁,写会加写锁

5. MVCC

MVCC(Multi-Version Concurrency Control)即多版本并发控制,这是 MySQL 为了提高数据库并发性能而实现的。它可以在并发读写数据库时,保证不同事务的读-写操作并发执行,同时也能解决脏读、不可重复读、幻读等事务隔离问题。

在前文讨论幻读的时候提到过当前读的概念,正是由于当前读,才会在可重复读的隔离级别下也会发生幻读的情况。

在解释可重复读隔离级别下发生幻读的原因之前,首先介绍 MVCC 的实现原理。

5.1 MVCC 的实现原理

首先我们需要知道,InnoDB 的数据页中每一行的数据是有隐藏字段的:

  • DB_ROW_ID: 隐式主键,若表结构中未定义主键,InnoDB 会自动生成该字段作为表的主键
  • DB_TRX_ID: 事务ID,代表修改此行记录的最后一次事务ID
  • DB_ROLL_PTR: 回滚指针,指向此行记录的上一个版本(上一个事务ID对应的记录)
  • 每一条修改语句都会相应地记录一条回滚语句(undo log),如果把每一条回滚语句视为一条数据表中的记录,那么通过事务ID和回滚指针就可以将对同一行的修改记录看作一个链表,链表上的每一个节点就是一个快照版本,这就是 MVCC 中多版本的意思。

    举个例子,假设对 user 表中唯一的一行「刺猬」进行多次修改。

    update user set name='重塑' where id=1;update user set name='木马' where id=1;update user set name='达达' where id=1;复制代码

    那么这条记录的版本链就是:

    在这个版本链中,头结点就是当前记录的最新版本。DB_TRX_ID 事务ID 字段是非常重要的属性,先 Mark 一下。

    除此之外,在读已提交(RC,Read Committed)和可重复读(RR,Repeatable Read)的隔离级别中,事务在启动的时候会创建一个读视图(Read View),用它来记录当前系统的活跃事务信息,通过读视图来进行本事务之间的可见性判断。

    在读视图中有两个重要的属性:

  • 当前事务ID:表示生成读视图的事务的事务ID
  • 事务ID列表:表示在生成读视图时,当前系统中活跃着的事务ID列表
  • 最小事务ID:表示在生成读视图时,当前系统中活跃着的最小事务ID
  • 下一个事务ID:表示在生成读视图时,系统应该分配给下一个事务的事务ID
  • 需要注意下一个事务I的值,并不是事务ID列表中的最大值+1,而是当前系统中已存在过的事务的最大值+1。例如当前数据库中活跃的事务有(1,2),此时事务2提交,同时又开启了新事务,在生成的读视图中,下一个事务ID的值为3。

    我们通过将版本链与读视图两者结合起来,来进行并发事务间可见性的判断,判断规则如下(假设现在要判断事务A是否可以访问到事务B的修改记录):

  • 若事务B的当前事务ID小于事务A的最小事务ID的值,代表事务B是在事务A生成读视图之前就已经提交了的,所以事务B对于事务A来说是可见的。
  • 若事务B的当前事务ID大于或等于事务A下一个事务ID的值,代表事务B是在事务A生成读视图之后才开启,所以事务B对于事务A来说是不可见的。
  • 若事务B的当前事务ID在事务A的最小事务ID下一个事务ID之间(左闭右开,[最小事务ID, 下一个事务ID)),需要分两种情况讨论:
  • 若事务B的当前事务ID在事务A的事务ID列表中,代表创建事务A时事务B还是活跃的,未提交,所以事务B对于事务A来说是不可见的。
  • 若事务B的当前事务ID不在事务A的事务ID列表中,代表创建事务A时事务B已经提交,所以事务B对于事务A来说是可见的。
  • 如果事务B对于事务A来说是不可见的,就需要顺着修改记录的版本链,从回滚指针开始往前遍历,直到找到第一个对于事务A来说是可见的事务ID,或者遍历完版本链也未找到(表示这条记录对事务A不可见)。

    这就是 MVCC 的实现原理。

    5.2 读视图的创建时机

    这里需要注意的是读视图的创建时机,在上面的论述中我们已经知道事务在启动时会创建一个读视图(Read View),而开启一个事务有两种方式,一是 begin/start transaction,二是start transaction with consistent snapshot,通过这两种方式开启事务,创建读视图的时机也是不同的:

  • 如果是以 begin/start transaction 方式开启事务,读视图会在执行第一个快照读语句时创建
  • 如果以 start transaction with consistent snapshot 方式开启事务,同时便会创建读视图
  • 5.3 MVCC 的运行过程

    为了详细说明 MVCC 的运行过程,下面举个例子,假设当前存在有两个事务(事务隔离级别为 MySQL 默认的可重复读):

    这里需要注意的是事务的启动时机,在上面的论述中我们已经知道事务在启动时会创建一个读视图(Read View),而开启一个事务有两种方式,一是 begin/start transaction,二是start transaction with consistent snapshot,通过这两种方式开启事务,创建读视图的时机也是不同的:

  • 如果是以 begin/start transaction 方式开启事务,读视图会在执行第一个快照读语句时创建
  • 如果以 start transaction with consistent snapshot 方式开启事务,同时便会创建读视图
  • 时刻事务A事务B
    1start transaction with consistent snapshot;
    2
    start transaction with consistent snapshot;
    3
    update user set name='重塑' where id=1;
    4select name from user where id=1;(N1)
    5
    commit;
    6select name from user where id=1;(N2)
    7commit;

    然后根据上面所描述的版本链以及两个事务开启时的读视图来分析 MVCC 的运行过程。

    上图是两个事务开启时的读视图,而当事务B的更新语句执行之后,id=1行的版本链如下所示。

    先来看N1处的查询语句,事务B的当前事务ID=2,其值等于事务A的下一个事务ID,所以按照上文中所论述的可见性判断,事务B对于事务A来说是不可见的,需要循着当前行的版本链网上检索。

    于是循着版本链来到DB_TRX_ID=1事务ID=1的历史版本,恰巧等于事务A的事务ID值,也就是事务A开启时该行的版本,此版本对于事务A来说当然是可见的,所以读取到了id=1行的name='刺猬',即最终N1=刺猬。

    再来看N2处的查询语句,此时事务B已提交,版本链还是如上图所示,由于当前版本的事务ID等于事务A读视图中的下一个事务ID,所以当前版本的记录对于事务A来说是不可见的,所以同样N2=刺猬。

    这里需要注意的是,若例子中事务A的时刻4语句变更为对该行的更新语句,那么事务A便会等待事务B提交之后再执行更新语句,这是因为事务B未提交,即事务B对于id=1行的写锁未释放,而事务A也要更新该行,必须是更新当前的最新版本(当前读)才可以,所以事务A就被阻塞了,必须等待事务B对该行的写锁释放,才会继续执行更新语句。

    5.4 RC 与 RR 生成读视图的时机对比

    上面所讨论的 MVCC 运行过程都是针对可重复读(RR, Repeatable Read)隔离级别的,如果是读已提交(RC, Read Committed)级别呢?

    上文中已经讨论过读已提交隔离级别中关于不可重复读的情况了,这里就不再举例,直接给出结论就可以了。

  • 可重复读(RR, Repeatable Read)隔离级别下生成读视图(Read View)的时机是开启事务的时候
  • 读已提交(RC, Read Committed)隔离级别下生成读视图(Read View)的时机是每一条语句执行前
  • 对于上文中描述 MVCC 执行过程中的例子,如果隔离级别是读已提交(RC, Read Committed):

  • N1处的查询语句,由于事务B还未提交,事务A可见的版本依旧是事务ID=1的版本,所以N1=刺猬
  • N2处的查询语句,事务B已提交,N2处查询语句执行时也会生成读视图,其当前事务ID=3,而在该记录的版本链中,当前版本的事务ID DB_TRX_ID=2,在N2查询语句事务ID之前,是可见的,所以N2=重塑
  • 5.5 当前读与快照读

  • 当前读:读取记录的最新版本
  • 快照读:读取记录时会根据一定规则读取事务可见版本的记录
  • 5.6 可重复读发生幻读的原因

    在理解了 MVCC 之后,我们再来看在可重复读隔离级别下发生幻读的原因。上文中说到正是由于当前读,才会在可重复读的隔离级别下发生幻读的情况,首先来回顾一下例子。

    时刻事务A事务B
    1begin;
    2select name from user;(N1)
    3
    begin;
    4
    insert into user values(2, '五条人');
    5
    commit;
    6select name from user;(N2)
    7select name from user for update;(N3)
    8commit;

    N1,N2处的查询想必已经十分明确都是「刺猬」了。而在N3处所使用的查询语句是for update,使用它进行查询就会对目标记录添加一把「行级锁」,行级锁的意义以后再说,现在只需要知道for update能够锁住目标记录就可以了。

    加锁自然是防止别人修改,那么理所当然,锁住的当然也就是记录的最新版本了。所以,在使用for update进行查询的时候,会使用当前读,读到目标记录的最新版本,所以在N3处的查询语句就会把事务B中本对于事务A来说不可见的记录也查询出来,也就发生了幻读。

    使用当前读的语句有:

  • select ... for update
  • select ... lock in share mode(共享读锁)
  • update ...
  • insert ...
  • delete ...
  • 6. 温故知新

    1. 事务的ACID四大特性
    2. 自动提交与隐式提交(隐式提交的语句)
    3. 事务的隔离级别
    4. 事务各个隔离级别可能出现的问题
    5. MVCC 的实现原理与过程
    6. RC 与 RR 生成读视图的时机

    更多相关免费学习推荐:mysql教程(视频)

    文档

    我所理解的MySQL之四:事务、隔离级别及MVCC

    我所理解的MySQL之四:事务、隔离级别及MVCC:.markdown-body{word-break:break-word;line-height:1.75;font-weight:400;font-size:15px;overflow-x:hidden;color:#333}.markdown-body h1.markdown-body h2.markdown-body h3.markdown-body h4.markdown-body h5.markdown-body h6{line-height:1.5;ma
    推荐度:
    标签: 理解 mysql 事务
    • 热门焦点

    最新推荐

    猜你喜欢

    热门推荐

    专题
    Top