原创

MySQL之可见性算法

温馨提示:
本文最后更新于 2024年01月21日,已超过 367 天没有更新。若文章内的图片失效(无法正常加载),请留言反馈或直接联系我

MySQL之可见性算法

前面我们了解了MySQL的各种日志以及底层是如何使用日志来实现灾难恢复即数据一致性。MySQL日志除了可以用来实现灾难恢复,还可以用来控制不同事务之间的隔离性,下面我们就来分析一下

首先我们先了解一下 ReadView 的三个重要全局属性:

  • trx_list:生成 ReadView 时正活跃的事务集合
  • up_limit_id:trx_list中最小的事务ID
  • low_limit_id:生成 ReadView 时系统尚未分配的下一个事务ID

我们知道数据库除了我们自定义的字段外,还有数据库本身定义的 DB_TRX_IDDB_ROLL_PTRDB_ROW_ID等隐藏字段;下面我们先讲一下可见性算法判断逻辑:

  • 比较 DB_TRX_ID < up_limit_id,成立则当前事务看到 DB_TRX_ID 记录,不成立则进入下一个判断
  • 比较 DB_TRX_ID >= low_limit_id,成立则当前事务不能看到 DB_TRX_ID 记录,不成立则进入下一个判断
  • 判断 DB_TRX_ID 是否在活跃事务集合trx_list中,则说明 ReadView生成时刻,DB_TRX_ID 处于活跃未提交状态,当前事务不能看到 DB_TRX_ID 记录;如果不在则说明生成 ReadView 时 DB_TRX_ID 已经提交,当前事务可以看到 DB_TRX_ID 记录

当我们同时开启多个事务对数据库进行操作;假设数据库有一条 id = 1,age = 21 的用户记录,事务1对这条数据进行查询,事务2对这条用户数据进行update age=22的修改操作,如下图:
多事务并发执行

根据可见性算法,分析 T3 时刻和 T5 时刻查询结果:

  • T3 时刻 事务1事务2 都处于活跃状态,此时 最小活跃事务IDup_limit_id1,尚未分配的下一个事务id low_limit_id3,活跃中事务集合 trx_list1,2DB_TRX_ID2
    • DB_TRX_ID < up_limit_id 不成立,我们进行下一个判断
    • DB_TRX_ID >= low_limit_id 不成立,我们继续下一个判断
    • DB_TRX_ID 在活跃事务集合 trx_list 中,此时我们看不到事务2中的数据,查询结果为 age = 21

  • T5 时刻 事务2 已经提交为非活跃状态,只有 事务1 处于活跃状态,此时活跃中事务集合 trx_list1,根据可见性算法
    • DB_TRX_ID < up_limit_id 不成立,我们进行下一个判断
    • DB_TRX_ID >= low_limit_id 不成立,我们继续下一个判断
    • DB_TRX_ID 不在活跃事务集合 trx_list 中,此时我们可以看到事务2提交的数据,查询结果为 age = 22

我们知道数据库有四种隔离级别,不同的隔离级别可以解决不同问题。隔离级别越高,数据一致性越好,但并发性越低。四种隔离级别分别是:

  • 读未提交(READ UNCOMMITTED、RU):可以读取其它事务未提交的脏数据。会产生脏读、不可重复读和幻读等问题
  • 读已提交(READ COMMITTED、RC ):可以读取其它事务已经提交的数据。可以避免产生脏读,但会产生不可重复读和幻读等问题
  • 可重复读(REPEATABLE READ、RR ):MySQL默认隔离级别。确保在同一个事务内多次读取同一条数据时,获得的结果是相同的,从而避免产生脏读和不可重复读的问题,但任然会产生幻读
  • 串行化(SERIALIZABLE):不同事务串行化执行,即一个事务必须在没有任何其它事务正在执行的条件下才能执行自己的事务,否则要等正在执行的事务执行完成后才能执行自己的事务。效率最低,但解决了脏读、不可重复读和幻读等问题

我们知道上述案例在不同隔离级别(如:RR 和 RC)下其结果并不相同,RC T5 得到的结果是 age = 22, 而 RR T5 得到的结果是 age = 21。其本质原因在于RR 隔离级别下,T5 时刻复用了 T3 时刻的ReadView,而 RC 隔离级别下,T5 时刻会生成新的ReadView

正文到此结束
本文目录