• MySQL之事务隔离级别和MVCC


    事务隔离级别

    事务并发可能出现的问题

    • 脏写 事务之间对增删改互相影响
    • 脏读 事务之间读取其他未提交事务的数据
    • 不可重复读 一个事务在多次执行一个select读到的数据前后不相同。因为被别的未提交事务修改,删除数据或数据被更新被当前事务读取到了。
    • 幻读 一个事务在第一次读取正常数据,第二次读取到其他未提交事务的insert记录,导致读取一个不存在的记录。指一次读取读取到了之前未读取到的数据。

    事务的4个隔离级别,以及解决的问题

    • READ UNCOMMITTED 未提交读 解决脏写
    • READ COMMITTED提交读 解决脏写、脏读
    • REPEATABLE READ可重复读 解决脏写、脏读、不可重复读
    • SERIALIAZBLE可串行化 解决脏写、脏读、不可重复读、幻读

    四个隔离级别和可以解决的问题是SQL专门规定的,但是在Innodb引擎下,在可重复读的隔离级别的下就可以直接解决幻读的问题。

    我们可以在启动时指定系统参数修改系统默认的隔离级别,默认为可重复读。

    mysql> show variables like 'transaction_isolation';
    +-----------------------+-----------------+
    | Variable_name         | Value           |
    +-----------------------+-----------------+
    | transaction_isolation | REPEATABLE-READ |
    +-----------------------+-----------------+
    1 row in set, 1 warning (0.00 sec)
    

    MVCC

    版本链

    我们在前面就讲过了undo日志,对于每次进行增删改就会产生undo日志,这时每个数据行的roll_pointer就会指向一个undo链表,我们就称其为版本链。我们在提一嘴,因为insert的undo日志在提交后是没有用的,所以在事务提交后insert的undo就会被释放。

    可以到前面的文章了解一下undo日志。大概知道undo日志的类型和产生的过程就OK了感觉怎么存储undo日志的那一部分讲得云里雾里https://www.cnblogs.com/duizhangz/p/16333565.html

    为什么没用?因为插入并不维护旧值,只是表明一个插入,并不需要存储什么信息。所以在事务提交时直接释放掉。因为事务在回滚时需要由一条insert语句类型的undo进行回滚。

    image

    这就是上面俩事务生成的版本链。

    image

    undo链表头存储的就是最新事务更新的记录信息。

    ReadView

    对于不同的事务隔离级别,我们可以读取的记录数据是不一样的。

    • 对于未提交读的隔离级别来说,我们可以直接读到数据的最新版本。

    • 对于提交读的隔离级别来说,我们需要可以读到的就是已经提交的事务的修改数据。

    • 对于可重复读的隔离级别来说,我们需要可以读到在事务开启前已经提交的事务的数据。

    • 对于串行读的隔离节别来说,Innodb采用加锁的方式来保证串行读。

    对于中间两个隔离级别,就需要ReadView这个结构来实现MVCC。以下是ReadView的结构

    • m_ids : 表示在生成ReadView时当前系统中活跃的读写事务ID的列表
    • min_trx_id : 表示在生成ReadView时当前系统中活跃的读写事务ID的最小值即在m_ids中最小的事务ID。
    • max_trx_id : 表示生成ReadView时系统中应该分配给下一个事务的ID。
    • creator_trx_id : 表示生成该ReadView的事务ID。

    有了ReadView这个结构,我们在就可以对事务进行控制。

    • 当被访问的记录行的事务ID大于等于max_trx_id,说明当前数据行不可见。
    • 当被访问的记录行的事务ID小于min_trx_id,可以直接获得数据。
    • 当被访问的记录行的事务ID等于creator_trx_id,说明是当前事务修改的记录,可以直接访问。
    • 当被访问的记录行的事务ID大于min_trx_id且小于max_trx_id,我们需要判断一下这个事务ID是不是在m_ids列表中,因为由可能是一个很早的事务很久还执行不介绍,导致中间的事务都结束了,如果在m_ids列表中,说明是活跃的,当前记录行不能访问,否则可以访问数据行。

    当前情况在版本链中从头到尾遍历,直到获得到数据。

    上面提到的提交读和可重复读,因为是两个隔离级别有区别的,两个隔离级别的实现只要在ReadView的时机上进行把控,就可以实现。

    • 提交读,我们只要保证当前事务的每个语句能读到已经提交的事务的数据。就可以在每个查询数据进行前,事务就会创建一个ReadView。
    • 可重复读,我们只要保证当前事务每个语句能读到事务开启前的数据,就可以在事务第一次读取数据时会创建ReadView,然后整个事务只会使用这个ReadView去判断能读取的数据行。

    仔细品一品,一下就可以恍然大悟。

    这个MVCC只会在我们使用普通select查询才会生效。


    __EOF__

  • 本文作者: captain
  • 本文链接: https://www.cnblogs.com/duizhangz/p/16333589.html
  • 关于博主: 评论和私信会在第一时间回复。或者直接私信我。
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
  • 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。
  • 相关阅读:
    金仓数据库KingbaseES客户端编程接口指南-Gokb (4. 执行SQL语句 )
    PID控制器改进笔记之七:改进PID控制器之防超调设定
    Stellar Toolkit for MySQL 9.0 Crack 3in1
    JavaScript语法知识笔记(三)——条件运算符,流程控制语句,循环
    ARouter 最新问题合集
    ROS2 从头开始:第3部分 — 创建自定义消息(.msg 和 .srv)和 Turtlebot3 服务节点的实践指南
    洛谷P5731 【深基5.习6】蛇形方阵java版题解
    GeoGebra 实例 时钟
    mongodb_exporter +prometheus
    eclipse 源代码文件报错处理
  • 原文地址:https://www.cnblogs.com/duizhangz/p/16333589.html