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

事务的隔离级别

事务由哪些特性

  • 原子性(Atomicity):一个事务中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节,而且事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样

  • 一致性(Consistency):是指事务操作前和操作后,数据满足完整性约束,数据库保持一致性状态。

  • 隔离性(Isolation):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致,因为多个事务同时使用相同的数据时,不会相互干扰,每个事务都有一个完整的数据空间,对其他并发事务是隔离的。

  • 持久性(Durability):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

  • InnoDB 引擎通过什么技术来保证事务的这四个特性的呢?

    • 持久性是通过 redo log (重做日志)来保证的;

    • 原子性是通过 undo log(回滚日志) 来保证的;

    • 隔离性是通过 MVCC(多版本并发控制) 或锁机制来保证的;

    • 一致性则是通过持久性+原子性+隔离性来保证;

并行事务会引发什么

  • 在同时处理多个事务的时候,就可能出现脏读(dirty read)、不可重复读(non-repeatable read)、幻读(phantom read)的问题。

  • 脏读

    • 如果一个事务「读到」了另一个「未提交事务修改过的数据」,就意味着发生了「脏读」现象。

    • 如果在上面这种情况事务 A 发生了回滚,那么事务 B 刚才得到的数据就是过期的数据,这种现象就被称为脏读。

  • 不可重复读

    • 在一个事务内多次读取同一个数据,如果出现前后两次读到的数据不一样的情况,就意味着发生了「不可重复读」现象。

    • 在这过程中如果事务 B 更新了这条数据,并提交了事务,那么当事务 A 再次读取该数据时,就会发现前后两次读到的数据是不一致的,这种现象就被称为不可重复读。

  • 幻读

    • 在一个事务内多次查询某个符合查询条件的「记录数量」,如果出现前后两次查询到的记录数量不一样的情况,就意味着发生了「幻读」现象。

事务的隔离级别由哪些

  • 脏读、不可重复读、幻读」的现象严重性排序

  • 使用四种隔离级别来规避这些现象,隔离级别越高,性能效率就越低

    • 读未提交(read uncommitted),指一个事务还没提交时,它做的变更就能被其他事务看到;

    • 读提交(read committed),指一个事务提交之后,它做的变更才能被其他事务看到;

    • 可重复读(repeatable read),指一个事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的,MySQL InnoDB 引擎的默认隔离级别;只能看见启动事务时的数据

    • 串行化(serializable );会对记录加上读写锁,在多个事务对这条记录进行读写操作时,如果发生了读写冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行;

    • 不同的隔离级别,并发事务时可能发生的现象也会不同

    • MySQL 虽然支持 4 种隔离级别,但是与SQL 标准中规定的各级隔离级别允许发生的现象却有些出入

  • 如何设置事务的隔离级别

    • 修改事务的隔离级别:SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL level;

      • level: {REPEATABLE READ | READ COMMITTED | READ UNCOMMITTED | SERIALIZABLE }

    • 在 SET 关键字后可以放置 GLOBAL 关键字、 SESSION 关键字或者什么都不放,这样会对不同范围的事务产生不同的影响

      • 使用 GLOBAL 关键字(在全局范围影响)

      • 使用 SESSION 关键字(在会话范围影响)

      • 上述两个关键字都不用(只对执行语句后的下一个事务产生影响)

  • MySQL InnoDB 引擎的默认隔离级别虽然是「可重复读」,但是它很大程度上避免幻读现象(并不是完全解决了)解决的方案有两种:

    • 针对快照读(普通 select 语句),是通过 MVCC 方式解决了幻读,因为可重复读隔离级别下,事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的,即使中途有其他事务插入了一条数据,是查询不出来这条数据的,所以就很好了避免幻读问题。

    • 针对当前读(select ... for update 等语句),是通过 next-key lock(记录锁+间隙锁)方式解决了幻读,因为当执行 select ... for update 语句的时候,会加上 next-key lock,如果有其他事务在 next-key lock 锁范围内插入了一条记录,那么这个插入语句就会被阻塞,无法成功插入,所以就很好了避免幻读问题。

  • 四种隔离级别具体是如何实现的呢?

    • 对于「读未提交」隔离级别的事务来说,因为可以读到未提交事务修改的数据,所以直接读取最新的数据就好了

    • 对于「串行化」隔离级别的事务来说,通过加读写锁的方式来避免并行访问

    • 对于「读提交」和「可重复读」隔离级别的事务来说,它们是通过 Read View 来实现的,它们的区别在于创建 Read View 的时机不同,可以把 Read View 理解成一个数据快照,就像相机拍照那样,定格某一时刻的风景。「读提交」隔离级别是在「每个语句执行前」都会重新生成一个 Read View,而「可重复读」隔离级别是「启动事务时」生成一个 Read View,然后整个事务期间都在用这个 Read View。

  • 执行「开始事务」命令,并不意味着启动了事务。在 MySQL 有两种开启事务的命令,分别是:

    • begin/start transaction 命令

      • 事务的启动时机:执行了第一条 select 语句,才是事务真正启动的时机

    • start transaction with consistent snapshot 命令

      • 事务的启动时机:执行了 start transaction with consistent snapshot 命令,就会马上启动事务

Read View 在 MVCC 里是如何工作的?

  • 假如另一个事务已经修改了记录但是尚未提交, 是不能直接读取最新版本的记录的,核心问题就是:需要判断一下版本链中的哪个版本是当前事务可见的,设计 InnoDB 的人提出了一个 ReadView 的概念。

  • Read View 结果及其作用

    • m_ids :指的是在创建 Read View 时,当前数据库中「活跃事务」的事务 id 列表,注意是一个列表,“活跃事务”指的就是,启动了但还没提交的事务

    • min_trx_id :指的是在创建 Read View 时,当前数据库中「活跃事务」中事务 id 最小的事务,也就是 m_ids 的最小值。

    • max_trx_id :这个并不是 m_ids 的最大值,而是创建 Read View 时当前数据库中应该给下一个事务的 id 值,也就是全局事务中最大的事务 id 值 + 1;

    • creator_trx_id :指的是创建该 Read View 的事务的事务 id。

  • 聚簇索引记录中的两个隐藏列

    • trx_id,当一个事务对某条聚簇索引记录进行改动时,就会把该事务的事务 id 记录在 trx_id 隐藏列里;

    • roll_pointer,每次对某条聚簇索引记录进行改动时,都会把旧版本的记录写入到 undo 日志中,然后这个隐藏列是个指针,指向每一个旧版本记录,于是就可以通过它找到修改前的记录。

  • 一个事务去访问记录的时候,除了自己的更新记录总是可见之外,还有这几种情况(可见就是可以读取到数据)

    • 如果记录的 trx_id 值小于 Read View 中的 min_trx_id 值,表示这个版本的记录是在创建 Read View 前已经提交的事务生成的,所以该版本的记录对当前事务可见。

    • 如果记录的 trx_id 值大于等于 Read View 中的 max_trx_id 值,表示这个版本的记录是在创建 Read View 后才启动的事务生成的,所以该版本的记录对当前事务不可见。

    • 如果记录的 trx_id 值在 Read View 的 min_trx_idmax_trx_id 之间,需要判断 trx_id 是否在 m_ids 列表中:

      • 如果记录的 trx_id 在 m_ids 列表中,表示生成该版本记录的活跃事务依然活跃着(还没提交事务),所以该版本的记录对当前事务不可见。

      • 如果记录的 trx_id 不在 m_ids列表中,表示生成该版本记录的活跃事务已经被提交,所以该版本的记录对当前事务可见。

  • 通过「版本链」来控制并发事务访问同一个记录时的行为就叫 MVCC(多版本并发控制)

    • 对该记录每次更新后,都会将旧值放到一条 undo日志 中,就算是该记录的一个旧版本,随着更新次数的增多,所有的版本都会被 roll_pointer 属性连接成一个链表,我们把这个链表称之为 版本链 ,版本链的头节点就是当前记录最新的值。另外,每个版本中还包含生成该版本时对应的事务id ,这个信息很重要

  • MVCC整体操作的流程

    • 获取事务自己的版本号,也就是事务id

    • 获取到ReadView

    • 查询得到数据,然后与ReadView中的事务版本进行比较

    • 如果不符合ReadView的规则,就需要从Undo log中获取历史快照

    • 最后放回符合规则的数据

可重复读是如何工作的?

  • 可重复读隔离级别是启动事务时生成一个 Read View,然后整个事务期间都在用这个 Read View。

读提交是如何工作的?

  • 读提交隔离级别是在每次读取数据时,都会生成一个新的 Read View。

  • 事务期间的多次读取同一条数据,前后两次读的数据可能会出现不一致,因为可能这期间另外一个事务修改了该记录,并提交了事务。

http://www.lryc.cn/news/335511.html

相关文章:

  • 性能优化角度
  • Vue3 使用ElementUI 显示异常
  • 从0开始复习python~
  • 从零开始搭建后端信息管理系统(新手小白比如)
  • CentOS 7详细介绍。
  • JavaScript:nodeType节点/文档碎片对象模型-DcoumentFragment
  • JDK安全剖析之安全处理入门
  • 探索RAG:加强问答能力的新技术
  • 赛氪网|2024中国翻译协会年会“AI科技时代竞赛与就业”分论坛
  • 【Jmeter+Influxdb+Grafana性能监控平台安装与部署】
  • [挖坟]如何安装Shizuku和LSPatch并安装模块(不需要Root,非Magisk)
  • rhce复习3
  • CentOS 7 升级 5.4 内核
  • photoshop2022增效工具ICOFormat.8bi(PS ico插件)
  • LeetCode-146. LRU 缓存【设计 哈希表 链表 双向链表】
  • 如何在Python中import其他文件的实时值
  • NumPy进阶(二)
  • 计算机专业,不擅长打代码,考研该怎么选择?
  • SQL Server的详细使用教程
  • 挑错罐头=“害猫”!猫咪主食罐到底应该怎么选?
  • 43---SATA电路设计
  • think:该写什么样的blog
  • 【APUE】网络socket编程温度采集智能存储与上报项目技术------多路复用
  • GitHub 仓库 (repository) Pulse - Contributors - Network
  • C语言题目:阶乘数列求和(函数)
  • Unity与CocosCreator对比学习二
  • 01-Git 快速入门
  • Axure RP中的相关概念及高保真原型构建方法
  • Ruoyi-vue-pro Vue + nginx 二级目录部署到云服务器
  • leetcode2529--正整数和负整数的最大计数