当前位置: 首页 > article >正文

【MySQL】什么是事务?MVCC?

文章目录

  • 【MySQL】什么是事务?MVCC?
    • 1.事务有哪些特性?
    • 2.并行事务引发什么问题?
    • 3.事务的隔离级别?
    • 4.read view在MVCC里如何工作
      • 4.1 read view是什么?
      • 4.2 聚簇索引的隐藏列?
      • 4.3 MVCC如何判断行记录对某一个事务是否可见?
    • 其他问题
    • 视频理解

【MySQL】什么是事务?MVCC?

1.事务有哪些特性?

原子性、一致性、隔离性、持久性。

  • 原子性(Atomicity):原子性就是事务中的所有操作要么全部成功,要么全部不完成,不会结束在中间某个环节,原子性是由undo log日志保证的。
  • 一致性(Consistency):一致性就是事务执行前后,数据库的状态必须保持一致性,一致性是由原子性+隔离性+持久性以及业务保证的。
  • 隔离性(Isolation):隔离性是防止多个事务并发读写同一个数据的时候,导致数据不一致问题的发生,是由MVCC和锁保证的。
  • 持久性(Durability):持久性是保证事务完成后对数据的修改就是永久的,不会因为系统故障而丢失,持久性是由redo log日志保证的。

上述提到了一致性是由原子性、隔离性、持久性保证的,那么其它特性是由什么保证的?

事务的原子性是通过undo log实现,在事务未提交前,历史数据会记录在undo log中,如果事务执行过程中,出现了错误或用户执行了回滚,MySQL可以利用undo log将数据恢复到事务开启前的状态,从而保证事务原子性。

隔离性是由MVCC和锁保证的。可重复读级别下的快照读是通过MVCC来保证事务隔离性的;当前读是通过行级锁来保证事务隔离性的。

持久性是由redo log保证的,因为MySQL通过WAL(先写日志再写数据)机制,在修改数据的时候,会将本次对数据页的修改以redo log的形式记录下来,这个时候更新就算完成了,Buffer Pool的脏页会通过后台线程刷盘,即使在脏页还没刷盘的时候发生了数据库重启,由于修改操作都记录到了redo log,之前已提交的记录都不会丢失,重启后就通过redo log,恢复脏页数据,从而保证事务的持久性。

2.并行事务引发什么问题?

MySQL服务端允许多个客户端连接,那么就会出现同时处理多个事务的情况。这就会导致一些问题,比如脏读、不可重复读、幻读。

  • 脏读:一个事务【读到】了另一个【未提交事务修改过的数据】。
    [图片]

就是说,A开启事务并且更新之后,但是还没有提交事务,这时B开启事务并读到了这个更新的数据,但是A很有可能回滚,那么B得到的数据就是过期的,那么这种现象就是脏读。

  • 不可重复读:在一个事务内多次读取同一个数据,如果出现了【前后两次读到的数据不一样】的情况,就是不可重复读现象。(关注的是因为修改造成的不一致情况)
    [图片]

就是说,A启动事务后第一次get到这个数据,然后B事务就启动了,并对其更新后就提交事务,这时A未提交事务并进行了再次读取,结果get到的数据不一样,就是不可重复读。

  • 幻读:在一个事务内多次查询某个符合查询条件的【记录数量】,如果出现前后两次查询到的记录数量不一样,就意味发生了幻读。(关注的是因为增删造成的不一致情况)
    [图片]

看着上面的不可重复读和幻读,极其混淆,在我看来,不可重复读主要是因修改造成结果的不同,幻读是因增删造成的记录数不同。
那脏读和幻读的区别是什么?
脏读是一个事务读到了另一个未提交事务修改过的数据,如果另一个事务回滚了,那么刚才读到的数据与数据库的就不一致。幻读是前后两次查询的结果集的数量不同,是因为增删造成的。
总结一下

  1. 脏读:读到其他事务未提交的数据。(最严重)
  2. 不可重复读:前后读取的数据不一致。(其次)
  3. 幻读:前后读取的记录数量不一致。(最低)

3.事务的隔离级别?

我们知道了在并发情况下,多个事务会出现麻烦的问题。为了解决这些问题,SQL标准提出了四种隔离级别来规避这些现象。
MySQL默认隔离级别是可重复读,同时还支持读未提交、读提交、串行化。

  • 串行化(serializable):会对记录加上读写锁,在多个事务对这条记录进行读写操作时,如果发生了读写冲突的时候,后访问的事务必须等前一个事务完成,才能继续执行。(避免了所有问题,但是事务并发性能最差)
  • 可重复读(repeatable read):指一个事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的。(避免了脏读和不可重复读,大程度上避免了幻读,没有完全避免)
  • 读提交(read committed):指一个事务提交后,它的变更才能被其它事务看到。(避免了脏读问题,但是还存在不可重复读和幻读这两个问题)
  • 读未提交(read uncommitted):指一个事务还没提交时,它的变更就能被其它事务看到。(什么也没解决)

MySQL的InnoDB存储引擎的默认隔离级别虽然是可重复读,但是它很大程度避免了幻读,但没有完全解决,虽然串行化可以解决,但是性能太差了,所以就有以下两种方式解决:

  1. 对于快照读(普通select),是通过MVCC方式解决幻读,因为可重复读隔离级别下,事务执行过程看到的数据,一直跟这个事务启动看到的数据是一致的,即使中途有其他事务插入一条数据,也是查询不出来的,所以就避免了幻读。
  2. 对于当前读(select … For update等),是通过next-key lock(记录锁+间隙锁)方式解决了幻读,因为当执行select … For update语句的时候,会加上next-key lock,如果有其他事务在next-key lock锁范围内插入一条记录,那么这个插入语句就会被阻塞,无法成功插入,所以就避免了幻读问题。

我们已经知道了这四种隔离级别了,那么他们又是如何实现的呢?

  • 对于【读未提交】隔离级别的事务,直接读取最新的数据即可。
  • 对于【串行化】,通过加读写锁来避免并行访问。
  • 对于【可重复读】和【读提交】,它们都是通过read view 实现的,主要区别就是它们的创建时机不同,read view可以看做是一个数据快照。【读提交】是在【每个语句执行前】都会重新生成一个read view,而【可重复读】是【启动事务时(START TRANSACTION 或 BEGIN )】生成一个read view,然后整个事务期间都使用这个read view。

4.read view在MVCC里如何工作

4.1 read view是什么?

Read view中有四个重要的字段:
[图片]

  1. creator_trx_id:创建该read view的事务id。
  2. m_ids:指的是在创建read view时,当前数据库中【活跃事务】的事务id列表,就是启动了但还没提交的事务。
  3. min_trx_id:指的是在创建了read view时,当前数据库中【活跃事务】中事务id最小的事务,也就是m_ids的最小值。
  4. max_trx_id:这个不是m_ids的最大值,而是创建read view 时当前数据库中应该给下一个事务的id值,也就是全局事务中最大的事务id+1 。

4.2 聚簇索引的隐藏列?

对于使用InnoDB存储引擎的数据库表,它的聚簇索引都有这两个隐藏列:

  1. trx_id:当一个事务对某条聚簇索引记录进行改动时,就会把事务的id记录到trx_id列里。
  2. roll_pointer:每次对某条聚簇索引记录进行改动时,都会把旧版本的记录写到undo log日志里,然后这个隐藏列执行每一个旧版本记录,于是就可以通过它找到修改前的记录。

4.3 MVCC如何判断行记录对某一个事务是否可见?

[图片]

Read view创建好之后,所有记录的trx_id就可以划分为这三种情况。
一个事务去访问记录的时候,除了自己更新的记录总是可见之外,也有以下规则:

  1. 如果记录的trx_id(隐藏列的) < read view 中的 min_trx_id ,表示这个版本的记录是在创建read view前就已经提交的事务生成的,所以对其可见。
  2. 如果记录的trx_id >= read view 中的 max_trx_id,表示这个版本的记录是在创建read view后才启动的事务生成的,所以不可见。
  3. 如果记录的trx_id in 【min_trx_id,max_trx_id】,需要判断trx_id是否在 m_ids 列表中;
  4. 如果在,表示生成这个版本记录的事务依旧活跃(还未提交事务),所以对其不可见。
  5. 如果不在,表示生成这个版本记录的事务已经提交,所以对其可见。

最后,以上这种通过【版本链】来控制并发事务访问同一个记录的行为就叫MVCC(多版本并发控制)。

其他问题

1、可重复读是如何实现的?
2、读提交是如何工作的?
3、当前读是如何避免幻读的?
4、可重复读隔离级别为什么不能完全避免幻读?

本篇文章借助以下的博客学习记录,膜拜大佬的一天。

事务隔离级别是怎么实现的?

MySQL 可重复读隔离级别,完全解决幻读了吗?

视频理解

1.脏读:166-读未提交隔离性下的演示_哔哩哔哩_bilibili
2.不可重复读:167-读已提交和可重复读的隔离性下的演示_哔哩哔哩_bilibili
3.幻读:168-幻读的演示与解决方案_哔哩哔哩_bilibili
4.MVCC:
184-MVCC三剑客:隐藏字段、UndoLog版本链、ReadView规则_哔哩哔哩_bilibili
186-MVCC在可重复读下解决幻读的流程_哔哩哔哩_bilibili


http://www.kler.cn/a/465145.html

相关文章:

  • Linux驱动开发(16):输入子系统–电容触摸驱动实验
  • 三子棋游戏
  • 服务器迁移中心——“工作组迁移”使用指南
  • Maven 教程之 pom.xml 详解
  • 大模型系列17-RAGFlow搭建本地知识库
  • NLP 中文拼写检测纠正论文-07-NLPTEA-2020中文语法错误诊断共享任务概述
  • Ceph对象存储接口的路线
  • 直观解读 JuiceFS 的数据和元数据设计(一)
  • LWM2M---Wakaama源码对接华为云平台
  • 推荐几个 docker 镜像加速地址
  • 【Vue】Composition API 钩子
  • vim、watch、cp和mv
  • df.replace({‘b‘: r‘\s*(\.)\s*‘}, {‘b‘: r‘\1ty‘}, regex=True)
  • vue中的h
  • CES Asia 2025:科技盛宴引领未来,BESTAR声学创新备受瞩目
  • 时间关系推理:利用大型语言模型检测股票投资组合崩溃
  • FFmpeg 4.3 音视频-多路H265监控录放C++开发二十一.4,SDP协议分析
  • C++如何读取CSV文件
  • jQuery get 方法内操控vue变量(异步ajax请求方法中操控双向绑定的响应式变量)实现异步请求函数内完成变量的双向响应式绑定
  • ElasticSearch05-集群搭建
  • 大模型 Fine-Tuning 技术解析
  • 【LLM】一文了解 NLP 里程碑模型 BERT
  • 太速科技-638-基于 KU060的双路1Gsps 14bit AD采集 PCIe卡
  • .net core修行之路-多线程异步编程概念篇
  • TDengine 如何进行高效数据建模
  • 什么是Sight Words(信号词)