一、事务概念

  事务是一组操作的集合,他是一个不可分割的工作单位,事务会把所有操作作为一个整体一起向系统提交或者撤销请求操作,即这些操作要么同时成功,要么同时失败。

二、事务特性

  • 原子性(Atomicity):事务是不可分割的最小操作单元,要么全部成功,要么全部失败
  • 一致性(Consistency):事务完成时,必须是所有的数据保持一致性状态
  • 隔离性(Isolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行
  • 持久性(Durability):事务一旦提交或回滚,他对数据库中的数据的该表就是永久的

三、事务的隔离级别

  • 读未提交(Read Uncommitted):最低的隔离级别,事务可以读取到其他事务尚未提交的数据,存在脏读(Dirty Read)问题,即读取到未提交的数据,可能导致数据的不一致性。
  • 读已提交(Read Committed):事务只能读取到其他事务已经提交的数据,解决了脏读的问题,但是可能导致不可重复读(Non-Repeatable Read)问题,即在同一事物中多次读取同一数据时可能会得到不同的结果。
  • 可重复读(Repeatable Read):可重复读是MySQL默认的隔离级别,并且解决了不可重复读问题,在一个事务中,多次读取同一数据会得到相同的结果,及时其他事务更改了数据。
  • 串行化读(Serializable):最高的隔离级别,强制所有事务按照顺序依次执行,避免了脏读,不可重读和幻读(Phantom Read),幻读指在同一事务中,多次查询同一个范围的数据时,结果集合的行数可能不一致。

四、Redo Log

  在MySQL中,Redo Log和Undo Log是用来支持事务和保证数据一致性的关键日志机制。

  Redo Log(重做日志): 作用是记录了所有对数据库的修改操作,包括插入、更新和删除等操作。记录了事务提交时数据页的物理修改,是用来实现事务的持久性

  该日志文件有两部分组成:重做日志缓冲(Redo Log Buffer)以及重做日志文件(Redo Log File),前者是在内存中,后者在磁盘中,当事务提交之后会把所有信息都存到该日志文件中,用户刷新脏页到磁盘,发生错误时,进行数据恢复使用。

  工作原理:当事务进行数据修改时,MySQL将修改的操作记录到 Redo Log中,而不直接写入磁盘的数据文件中。这样可以减少磁盘IO的操作,提高性能。在事务提交时,Redo Log的内容会被异步刷新到磁盘上的数据文件中,确保数据的持久性。如果系统崩溃,MySQL可以通过Redo Log中的信息重做之前未写入磁盘的修改操作,恢复到事务提交的状态。

五、Undo Log

  5.1 简介

    Undo Log(回滚日志):用来支持事务的回滚操作,即将事务的修改操作撤销,恢复到之前的状态。在insert、update、delete的时候产生的便于数据回滚的日志。

    当insert的时候,产生的 Undo Log日志只在回滚时需要,在事务提交后,可被立即删除。

    而update、delete的时候,产生的Undo Log日志不仅在回滚时需要,在快照读时也需要,不会立即被删除。

    工作原理:当事务进行数据修改时,MySQL将修改的操作记录到Undo Log中,并在事务提交之前保留这些修改的记录。如果事务发生回滚操作,MySQL会根据Undo Log中的信息,将事务的修改操作撤销,将数据还原到事务开始的状态。因此,Undo Log为事务提供了撤销操作的能力,确保数据库的一致性。

  5.2 Undo Log 版本链

    不同事务或相同事务对同一条记录进行修改,会导致该记录的Undo Log生成一条记录版本链,链表的头部是最新的旧记录,链表尾部是最早的旧记录

    

    说明1:第一次insert的数据,此时DB_TRX_ID 为1, 因为还没有修改过,所以DB_ROLL_PTR为null

    

    说明2:此时我们模拟几个并发同时开始的事务

    

    说明3:此时我们在事务2中修改id为30的数据

    

    说明4:那么在执行修改操作之前,会先把原始数据写入Undo Log日志中一份,方便数据回滚

    说明5:在此时原始记录中age改为3,并且隐藏字段DB_TRX_ID变为2,DB_ROLL_PTR在为Undo Log 日志中的第一条数据的地址

    

    说明6:此时事务2提交事务之后,事务3又修改id为30的数据,name改为A3

  

    说明7:此时在更改原始数据之前,需要把当前的数据在写入到Undo Log日志中一份,继续方便做数据回滚,而这时Undo Log中就有了两条数据

    说明8:然后就可以修改原始数据 name 改为 A3,并且DB_TRX_ID也改为3,DB_ROLL_PTR指向Undo Log第二条日志的地址。

    说明9:这样就形成了一条完整的Undo Log版本链

六、MVCC

  6.1 当前读

    读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行枷锁。

    如:select...lock in share mode(共享锁)、select...for update、update、insert、delete(排他锁)都是一种当前读。

    

  6.2 快照读

    简单的select(不加锁)就是快照读,快照读读取的是记录数据的可见版本,有可能是历史数据,不加锁,是非阻塞读。

    • Read Committed:每次select,都生成一个快照读。
    • Repeatable Read:开启事务后第一个select语句就会生成一个快照读,其后面的select读取的都是快照中的数据,所以数据才是可重复读的。
    • Serializable:快照读会退化为当前读      

  6.3 MVCC

    全称 Multi-Version Concurrency Control 多版本并发控制。指维护一个数据的多个版本,使得读写操作没有冲突,快照读为MySQL实现MVCC提供了一个非阻塞读功能。MVCC具体实现,还需要依赖于数据库记录中的三个隐式字段、Undo Log日志、readView。

  6.4 记录中的三个隐藏字段   

    示例如下: 

   

    DB_TRX_ID:最近修改事务ID,记录插入这条记录或最后一次修改该记录的事务ID

    DB_ROLL_PTR:回滚指针,指向这条记录的上一个版本,用于配合Undo Log,指向上一个版本

    DB_ROW_ID:隐式主键,如果表结构没有指定主键,将会生成该隐藏字段

    使用指令重新创建一个表mvcc_test,这个时候不给该表设置主键

    

    然后在数据库路径下的data文件夹下就可以找到我们刚创建的独立表结构空间mvcc_test.ibd

    

    MySQL提供了一个ibd2sdi 指令可以查看表结构空间

root@ubuntu:/usr/local/mysql/data/mysql_test# ibd2sdi mvcc_test.ibd
["ibd2sdi",
{
... # 这里的 ... 是我过滤掉的无关数据
"dd_object_type": "Table", # 这里说明这是一个表结构
"dd_object": {
"name": "mvcc_test",        # 名称 mvcc_test
 ...
"columns": [ # 所有的列
{
"name": "name", # 自己定义的name列
  ...
},
{
"name": "DB_ROW_ID", # 隐式创建的列
 ...
},
{
"name": "DB_TRX_ID", # 隐式创建的列,如果在创建表的时候指定了主键,则该隐藏字段就不会被创建
...
},
{
"name": "DB_ROLL_PTR", # 隐式创建的列
...
}
],
"schema_ref": "mysql_test", # 该表所属的数据库
    ...
}
}
]
root@ubuntu:/usr/local/mysql/data/mysql_test#

七、readView

  7.1 readView(读视图)

    是快照读SQL执行时MVCC提取数据的依据,记录并维护系统当前活跃的事务id,即未提交的事务id

  7.2 readView中的四个核心字段

    • m_ids:当前活跃的事务ID集合,即当前未提交的事务的ID集合。
    • min_trx_id:最小活跃事务ID。
    • max_trx_id:预分配事务ID,当前最大事务ID+1,因为事务ID是自增的。
    • creator_trx_id:readView创建者的事务ID。

  7.3 版本链数据访问规则

    • 当前事务ID == creator_trx_id 可以访问该版本:因为当前事务的id=creator_trx_id说明这个版本就是当前事务创建的。
    • 当前事务ID < min_trx_id 可以访问该版本:因为当前事务ID小于当前正在活跃事务的最小ID,说明当前事务已经提交了,并且该版本是在当前时候提交后才创建,所以可以访问。
    • 当前事务ID > max_trx_id 不可以访问该版本:因为当前事务ID比readView最大的活跃ID还大,说明当前事务是在readVIew创建之后才开启的,所以不能访问以前的还未提交事务的数据。
    • 当前事务ID >= min_trx_id 并且 <= max_trx_id 并且不在m_ids中 可以访问该版本:因为不在m_ids中说明数据已经提交了,并且<= max_trx_id 又说明不是在readView创建之后开启的,所以可以访问。

  7.4 不同隔离级别,生成readView的时机不同

    • read committed:在事务中每一次执行快照读时生成readView。
    • repeatable read:仅在事务中第一次执行快照读时生成readView,后续复用该readView。

  7.5 read committed隔离级别下readView查询数据的机制

    因为read committed 模式下, 在事务中每一次执行快照读时都会生成readView.所以在同一个事务下,读取两次数据,就会产生两个ReadVIew

    

    说明1:以事务5的两次查询为例

    说明2:ReadView1中的m_ids:[3,4,5],因为事务2在开启该次查询事务的事就已经提交了,所以m_ids中不包括2,同样的在ReadView2创建的时候,事务3也已经提交,所以ReadView2中的m_ids只有[4,5]

    说明3:套用版本链访问数据的规则,ReadView1和ReadView2两次查询数据演示

    说明4:ReadView1数据查询演示

    1. 使用DB_TRX_ID=4 的数据进行验证,4 不等于 creator_trx_id(5),所以第一个原则不满足
    2. 使用DB_TRX_ID=4 的数据进行验证,4 不小于 min_trx_id(3), 所以第二个原则也不满足
    3. 使用DB_TRX_ID=4 的数据进行验证,4 不大于 max_tr_id(6), 满足第三个原则,但是第三个原则是判断不能访问的,就算着这里满足也不一定能访问,还要看第四个原则
    4. 使用DB_TRX_ID=4 的数据进行验证,4 满足 >=3 并且 <=6 , 但是4在m_ids[3,4,5]中,所以ReadVIew1不能使用DB_TRX_ID = 4 的数据,通过表格也可以看出开在ReadView1查询的时候,事务4并没有提交呢
    5. 使用DB_TRX_ID=3 的数据进行验证,3 不等于 creator_trx_id(5),所以第一个原则不满足
    6. 使用DB_TRX_ID=3 的数据进行验证,3 不小于 min_trx_id(3), 所以第二个原则也不满足
    7. 使用DB_TRX_ID=3 的数据进行验证,3 不大于 max_tr_id(6), 满足第三个原则,但是第三个原则是判断不能访问的,就算着这里满足也不一定能访问,还要看第四个原则
    8. 使用DB_TRX_ID=3 的数据进行验证,3 满足 >=3 并且 <=6 , 但是3在m_ids[3,4,5]中,所以ReadVIew1不能使用DB_TRX_ID = 3 的数据,通过表格也可以看出开在ReadView1查询的时候,事务3并没有提交呢
    9. 使用DB_TRX_ID=2 的数据进行验证,2 不等于 creator_trx_id(5),所以第一个原则不满足
    10. 使用DB_TRX_ID=2 的数据进行验证,2 小于 min_trx_id(3), 所以第二个原则满足,则可以判断ReadView1使用的是 DB_TRX_ID = 2的数据,即id=30, age=3,name=A30

    说明5:ReadView2数据查询演示

    1. 使用DB_TRX_ID=4 的数据进行验证,4 不等于 creator_trx_id(5),所以第一个原则不满足
    2. 使用DB_TRX_ID=4 的数据进行验证,4 不小于 min_trx_id(4), 所以第二个原则也不满足
    3. 使用DB_TRX_ID=4 的数据进行验证,4 不大于 max_tr_id(6), 满足第三个原则,但是第三个原则是判断不能访问的,就算着这里满足也不一定能访问,还要看第四个原则
    4. 使用DB_TRX_ID=4 的数据进行验证,4 满足 >=4 并且 <=6 , 但是4在m_ids[4,5]中,所以ReadVIew2不能使用DB_TRX_ID = 4 的数据,通过表格也可以看出开在ReadView2查询的时候,事务4并没有提交呢
    5. 使用DB_TRX_ID=3 的数据进行验证,3 不等于 creator_trx_id(5),所以第一个原则不满足
    6. 使用DB_TRX_ID=3 的数据进行验证,3 不小于 min_trx_id(4), 所以第二个原则满足,则可以判断ReadView2使用的是 DB_TRX_ID = 3的数据,即id=30, age=3,name=A3。

  7.6 repeatable read 隔离级别下readView查询数据的机制

    repeatable read:仅在事务中第一次执行快照读时生成readView,后续复用该readView,所以这里只会产生一个ReadVIew

    

    说明6:因为这里的数据查询演示过程个上面的一样,只不过这里只有一个ReadVIew,过程更简单。

八、事务的实现原理

  8.1 MVCC的实现原理

    三个隐藏字段+Undo Log版本链+ReadView是MVCC的实现原理

  8.2 事务隔离性的实现原理

    MVCC+锁就是事务隔离性的实现原理

  8.3 事务原子性、一致性实现原理

    Undo Log + Read Log

  8.4 事务的持久性实现原理

    Read Log

MySQL高级12-事务原理的更多相关文章

  1. Mysql高级之事务

    原文:Mysql高级之事务 通俗的说事务: 指一组操作,要么都成功执行,要么都不执行.---->原子性 在所有的操作没有执行完毕之前,其他会话不能够看到中间改变的过程-->隔离性 事务发生 ...

  2. 五分钟详解MySQL并发控制及事务原理

    在如今互联网业务中使用范围最广的数据库无疑还是关系型数据库MySQL,之所以用"还是"这个词,是因为最近几年国内数据库领域也取得了一些长足进步,例如以TIDB.OceanBase等 ...

  3. MySQL数据库本地事务原理

    在经典的数据库理论里,本地事务具备四大特征: 原子性 事务中的所有操作都是以原子的方式执行的,要么全部成功,要么全部失败: 一致性 事务执行前后,所有的数据都应该处于一致性状态---即要满足数据库表的 ...

  4. MySQL高级查询 & 事务机制

    1.基础查询where 高级条件查询Where子句 SELECT empno,ename,sal,hiredate FROM t_tmp WHERE deptno=10 AND (sal+IFNULL ...

  5. MySQL中的事务原理和锁机制

    本文主要总结 MySQL 事务几种隔离级别的实现和其中锁的使用情况. 在开始前先简单回顾事务几种隔离级别以及带来的问题. 四种隔离级别:读未提交.读已提交.可重复读.可串行化. 带来的问题:脏读.不可 ...

  6. 「MySQL高级篇」MySQL之MVCC实现原理&&事务隔离级别的实现

    大家好,我是melo,一名大三后台练习生,死去的MVCC突然开始拷打我! 引言 MVCC,非常顺口的一个词,翻译起来却不是特别顺口:多版本并发控制. 其中多版本是指什么呢?一条记录的多个版本. 并发控 ...

  7. 「MySQL高级篇」MySQL锁机制 && 事务

    大家好,我是melo,一名大三后台练习生,最近赶在春招前整理整理发过的博客~! 引言 锁锁锁,到哪到离不开这桩琐事,并发琐事,redis琐事,如今是MySQL琐事,这其中琐事,还跟MySQL另一个重要 ...

  8. Mysql高级之权限检查原理

    原文:Mysql高级之权限检查原理 用户进行数据库操作分为两步: 1 是否有权限连接,根据host,name,password: 2 是否有权限进行CURD: 图示解说: 关于用户权限在哪里进行存放? ...

  9. MySQL事务原理&实战【官方精译】

    事务隔离级别 事务隔离是数据库处理的基础之一.隔离是I中的首字母 ACID ; 隔离级别是在多个事务同时进行更改和执行查询时,对结果的性能和可靠性,一致性和可重复性之间的平衡进行微调的设置. Inno ...

  10. 谈谈MySQL支持的事务隔离级别,以及悲观锁和乐观锁的原理和应用场景?

    在日常开发中,尤其是业务开发,少不了利用 Java 对数据库进行基本的增删改查等数据操作,这也是 Java 工程师的必备技能之一.做好数据操作,不仅仅需要对 Java 语言相关框架的掌握,更需要对各种 ...

随机推荐

  1. 高分辨率大图像可缩放 Web 查看器的实践

    高分辨率大图像可缩放 Web 查看器的实践 一.使用 vips 将高分辨率大图像转换为 DZI 安装 vips 具体安装步骤请参考libvips Install. 注意,在 windows 11 中安 ...

  2. 基于 Dash Bio 的生物信息学数据可视化

    Dash 是用于搭建响应式 Web 应用的 Python 开源库.Dash Bio 是面向生物信息学,且与 Dash 兼容的组件,它可以将生物信息学领域中常见的数据整合到 Dash 应用程序,以实现响 ...

  3. Rust 通用编程概念

    通用编程概念 变量.基本类型.函数.控制流 变量与可变性 rust中的变量默认是不可变的,这样是为了能够让你安全并且方便地写出复杂.甚至并行的代码. 当一个变量是不可变时,一旦它绑定到了某个值上面,这 ...

  4. @Override注解的使用

    先看看@Override注解在Java SE中的声明: package .lang; import java.lang.annotation.*; @Target(ElementType.METHOD ...

  5. 浅谈TCP和UDP

    简介 在计算机网络中,TCP(传输控制协议)和UDP(用户数据报协议)是两个常用的传输层协议.它们分别提供了可靠的数据传输和快速的数据传送,成为互联网世界中的双子星.本文将探讨TCP和UDP的特点.优 ...

  6. 洛谷 P4859 已经没有什么好害怕的了

    题目描述 学姐 4 了. 有 \(n\) 个糖果和 \(n\) 个药片,它们要进行一一配对.每个糖果或药片都具有互不相同的能量值,要求配对后,糖果比药片能量高的对数,比剩下的对数恰好多 \(k\),求 ...

  7. 将前端优化到最低:Web应用程序和移动应用程序最佳实践

    目录 1. 引言 2. 技术原理及概念 3. 实现步骤与流程 4. 应用示例与代码实现讲解 4.1. 应用场景介绍 4.2. 应用实例分析 4.3. 核心代码实现 4.4. 代码讲解说明 将前端优化到 ...

  8. 看懂java序列化,这篇就够了

    前言 相信大家日常开发中,经常看到 Java 对象 "implements Serializable".那么,它到底有什么用呢?本文带你全方位的解读序列化与反序列化这一块知识点. ...

  9. 聊聊JVM虚方法表和方法调用

    作者:小牛呼噜噜 | https://xiaoniuhululu.com 计算机内功.源码解析.科技故事.项目实战.面试八股等更多硬核文章,首发于公众号「小牛呼噜噜」 大家好,我是呼噜噜,好久没更新文 ...

  10. 【Kubernetes】yaml文件编写 -- 持续更新

    K8S通过yaml格式的声明式API与资源对象交互 API版本由apiVersion字段指定,API对象类型由kind字段指定 除此之外,每个API对象有三大类属性: metadata:元数据 spe ...