事务

事务是一组原子性的 SQL 操作,或者被称为一个独立的工作单元,如果数据库引擎能够成功地对数据库应用该组的全部 SQL 语句,那么就会全部执行,否则全部不执行。

事务的特性

在关系数据库管理系统中,事务需要满足 ACID 四个基本特征,具体解释如下[1]

  • A(Atomicity)原子性

    一个事务(transaction)中的所有操作,或者全部完成,或者全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。即,事务不可分割、不可约简

  • C(Consistency)一致性

    在事务开始之前和事务提交之后,数据库的完整性没有被破坏,即在事务发生前后数据依旧满足原有的约束条件、级联回滚等

  • I(Isolation)隔离性

    数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致的数据不一致。事务的隔离级别从低到高分为四个等级:读未提交(read uncommitted)、提交读(read committed)、可重复读(repeatable read)和可串行化(serializable)

  • D(Duration)持久性

    事务处理结束之后,对于数据的修改是永久的,即便系统发生故障也不会丢失

事务的状态

事务的可能状态如下图所示:

  • active:表示事务已经开始了,可以通过显式地执行 BEGINSTART TRANSACTION语句来开启一个事务
  • partially committed:部分提交状态,此时事务已经执行结束了,但是不会直接将最终结果直接写入到磁盘中,在这一步是将结果写入到内存中
  • committed:将数据刷新磁盘上,此步骤完成则表示确实是成功提交了事务
  • failed:事务执行过程中失败或者从内存写入到磁盘中的过程失败,此时需要执行回滚操作
  • aborted:回滚执行完成之后的状态

事务的隔离级别

事务并发执行时可能会存在的一些一致性问题:

  • 脏读

    一个事务读取了另一个事务还 没有提交 的数据,此时读取到的数据是脏数据,因此被称为 “脏读”

  • 不可重复读

    一个事务开始时,只能看见已经提交了的事务对数据所做的修改,未提交的事务对数据的修改不可见,当同一个事务两次读取同一个数据时,两次读取到的数据不一致。这是因为在这两次读取的时间间隔中,有其它的事务提交对该数据造成了修改,使得两次读取的数据不一致,这就被称为 ”不可重复读“

  • 幻读

    当某个事务在读取某个范围内的记录时,另外一个事务又在该范围内插入了新的记录,此时当前的事务再次读取该范围内的数据时,将会产生 “幻行” 问题,即两次读取出现了数据量不一致的情况,这就被称之为 ”幻读“

SQL 标准定义了以下四种隔离级别:

  • read uncommitted(读未提交)

    最低级别的隔离级别,在这种隔离级别中,即使事务还未提交,对于数据的修改对于其它事务来讲也是可见的,这种情况特别危险,一般情况下不要使用

  • read committed(提交读)

    所有的事务只能看到已经提交的事务对数据的修改,即一个事务从开始直到提交之前,所做的任何修改对于其它事物都是不可见的。在这种隔离级别下,避免了 ”脏读“ 问题的出现, 但是不能解决 ”不可重复读“ 问题

  • repeatable read(可重复读)

    在这种隔离级别下,避免了 ”脏读“ 和 ”不可重复读“ 问题的出现,这也是 MySQL 默认的事务隔离级别。SQL 标准并不要求在这种隔离级别下解决 ”幻读“ 的问题,但是 MySQL 通过 MVCC 的方式在这种隔离级别下解决了 ”幻读“ 的问题[2]

  • serializable(串行化)

    在串行化的隔离级别下,事务与事务之间通过串行的方式执行,从根源上避免了并发执行时出现的一系列问题,缺点在于由于串行化,使得事务的执行效率没有那么高,因此一般情况下也不会采用这种隔离级别

四种隔离级别的比较如下表所示:[4]

隔离级别 脏读 不可重复读 幻影读
未提交读 可能发生 可能发生 可能发生
提交读 - 可能发生 可能发生
可重复读 - - 可能发生
可序列化 - - -

MVCC

MVCC(Multi-Version Concurrency Control):多版本并发控制,通过记录的版本链和 ReadView (一致性视图)来控制并发事务访问相同的记录时的行为。MVCC 没有固定的标准,具体取决于各个数据库管理系统的具体实现

版本链

每次对数据执行操作之后,都会将旧值放入到放入到一条 undo 日志中,随着修改的次数增多,所有的版本都通过 recordroll_pointer 属性连接成为一条链表,这个链表就称为 ”版本链“

具体的示意图如下图所示:[3]

假设现在两个事务 T1 和 T2,两者的事务 id 分别为 trx=85 和 trx=90,现在 T1 开启了事务,准备将 tb_student 中 id = 5 的记录的 name 列的值首先更改为 "a",再更改为 "b",T2 也开启了事务,准备将 tb_student 中 id = 5 的记录的 name 的列的值首先修改为 ”c“,再修改为 ”d“。

假设在执行事务之前,id = 5 的记录中 name 属性的值为 "muse",现在按照 执行时刻 的顺序,首先 T1 的两次更新操作将会被执行,同时将记录写入到 undo log 中,形成对应的版本链(trx_id=85 的部分);然后,T2 在按照时间顺序继续执行更新操作,将记录写入到 undo log 中,与之前的 undo log 链形成最终的 undo log 链。注意将当前记录与 undo log 链的整体看做同一个链

ReadView

ReadView 中关键的四个属性:

  • m_ids:在生成 ReadView 时,当前系统中活跃的读写事务的事务 id 列表
  • min_trx_id:在生成 ReadView 时,当前系统中活跃的读写事务中 最小的事务 id,也就是 m_ids 中的最小值
  • max_trx_id:在生成 ReadView 时,系统应该分配给下一个事务的事务 id 的值
  • creator_trx_id:生成该 ReadView 的事务的事务 id(当前记录中的 trx_id

版本的可见性的规则:

  • 如果 trx_id == creator_trx_id,则表示当前事务正在访问它自己修改过的记录,所以该版本可以被当前的事务所访问
  • 如果 trx_id < min_trx_id,则表明生成该版本的事务在当前事务生成 ReadView 之前已经提交,所以该版本可以被当前的事务访问
  • 如果 trx_id >= max_trx_id,则表明生成该版本的事务在当前事务生成 ReadView 之后才开始,所以该版本不可以被当前事务访问
  • 如果 trx_idm_ids 中,说明创建 ReadView 的时候生成该版本的事务还是活跃的,该版本不可以被访问
  • 如果 trx_id 不在 m_ids 中,说明创建 ReadView 时生成该版本的事务已经被提交了,该版本可以被访问

如果某个版本的数据对于当前事务不可见,那么就顺着版本链找到下一个版本的数据,并继续通过以上的规则来判断记录的可见性,直到找到版本链中的最后一个版本

ReadView 的生成时机

Read Committed 和 Repeatable Read 隔离级别在 MVCC 上的最大区别在于 ReadView 的生成时机的不同,这种不同直接导致了这两种隔离级别对于 ”不可重复读“ 问题的处理。

对于 Read Committed,在一个事务中,每次读取数据之前都会生成一个 ReadView,这样的话就会使得其它事务对于数据的修改对于当前事务来讲也是可见的,因此存在 “不可重复读“ 的问题,而对于 Repeatable Read,在一个事务中,只有在第一次读取数据时生成一个 ReadView,这样就保证了在当前事务的执行过程中是无法看到别的事务对于数据的修改,这就避免了 “不可重复读” 问题的出现

参考:

[1] https://zh.wikipedia.org/wiki/ACID

[2] 《高性能 MySQL》(第三版)

[3] https://mp.weixin.qq.com/s/sxlq4kdOaCi5jdsWAnpiBw

[4] https://zh.wikipedia.org/wiki/事務隔離

MySQL 基础(三)事务与 MVCC的更多相关文章

  1. 04 mysql 基础三 (进阶)

    mysql 基础三 阶段一 mysql 单表查询 1.查询所有记录 select * from department; ​ select * from student; ​ select * from ...

  2. MySQL基础之事务编程学习笔记

    MySQL基础之事务编程学习笔记 在学习<MySQL技术内幕:SQL编程>一书,并做了笔记.本博客内容是自己学了<MySQL技术内幕:SQL编程>事务编程一章之后,根据自己的理 ...

  3. Mysql基础之 事务

    MySql事务 Mysql事务主要处理操作量大,复杂度高的数据. Mysql事务需要注意的三点: 1.在mysql中只有使用innodb数据库引擎的数据库或表才支持事务 2.事务处理可以用来维护数据库 ...

  4. mysql基础三(视图、触发器、函数、存储过程、事务、防注入)

    一.视图 视图是一个虚拟表(非真实存在),其本质是[根据SQL语句获取动态的数据集,并为其命名],用户使用时只需使用[名称]即可获取结果集,并可以将其当作表来使用. 1.创建视图 -格式:CREATE ...

  5. MySQL中的事务和MVCC

    本篇博客参考掘金小册--MySQL 是怎样运行的:从根儿上理解 MySQL 以及极客时间--MySQL实战45讲. 虽然我们不是DBA,可能对数据库没那么了解,但是对于数据库中的索引.事务.锁,我们还 ...

  6. mysql基础(三)——中级查询

    创建表 CREATE TABLE DEPT( DEPTNO ) PRIMARY KEY, DNAME ) , LOC ) ) ; ,'ACCOUNTING','NEW YORK'); ,'RESEAR ...

  7. mysql基础_事务

    定义 一个事务其实就是一个完整的业务逻辑,是一个最小的工作单元,不可再分,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败. 例如:王五向赵六的账户上转 ...

  8. 重新整理 mysql 基础篇————— 事务隔离级别[四]

    前言 简单介绍一下事务隔离的基本 正文 Read Uncommitted(未提交读) 这个就是读未提交.就是说在事务未提交的时候,其他事务也可以读取到未提交的数据. 这里举一个例子,还是前一篇的例子. ...

  9. MySQL 基础三 函数(聚合、字符串、时间、条件判断)

    1.聚合 其它:GROUP_CONCAT.avg.sum.count.max.min SELECT typeid,GROUP_CONCAT(goodsname) FROM `goods` GROUP ...

  10. Mysql基础(三)

    #DML语言 /* 数据操作语言 插入:insert insert into 表名(列名,...) values(值1,...); insert into 表名 set 列名=值, 列名=值,... ...

随机推荐

  1. 5.go语言函数提纲

    1 本篇前瞻 前端时间的繁忙,未曾更新go语言系列.由于函数非常重要,为此将本篇往前提一提,另外补充一些有关go新版本前面遗漏的部分. 需要恭喜你的事情是本篇学完,go语言中基础部分已经学完一半,这意 ...

  2. PostgreSQL快速导入千万条数据

    目录 一.测试环境 二.修改源数据为COPY可用的格式 三.DDL 四.COPY 五.结论 为了与MySQL做个对比,做一个PG的数据导入测试,使用COPY方式,测试环境保持一致,具体如下所述. 一. ...

  3. 再学Blazor——概述

    简介 Blazor 是一种 .NET 前端 Web 框架,同时支持服务器端呈现和客户端交互性. 使用 C# 语言创建丰富的交互式 UI 共享前后端应用逻辑 可以生成混合桌面和移动应用 受益于 .NET ...

  4. 开源项目 | 一款基于NodeJs+Vue3的强大的在线设计图片工具

    一.项目概述 一款漂亮且功能强大的在线海报图片设计器,仿稿定设计.适用于海报图片生成.电商分享图.文章长图.视频/公众号封面等多种场景. 二. 技术特性 丝滑的操作体验,丰富的交互细节,基础功能完善 ...

  5. oauth2单点登录集成

    单点登陆 概念: 单点登录其实就是在多个系统之间建立链接, 打通登录系统, 让同一个账号在多个系统中通用 举个例子: 登录Gmail的时候可以用账号密码登录, 也可以用google账号登录, 而使用g ...

  6. 文心一言 VS 讯飞星火 VS chatgpt (117)-- 算法导论10.3 2题

    二.用go语言,对一组同构对象用单数组表示法实现,写出过程ALLOCATE-OBJECT和FREE-OBJECT. 文心一言: 在Go语言中,我们通常使用指针和结构体来表示和操作复杂的数据结构.对于一 ...

  7. InnoDB 存储引擎之 Buffer Pool

    Mysql 5.7 InnoDB 存储引擎整体逻辑架构图 一.Buffer Pool 概述 InnoDB 作为一个存储引擎,为了降低磁盘 IO,提升读写性能,必然有相应的缓冲池机制,这个缓冲池就是 B ...

  8. 采药(lgP1048)

    emmm 01 背包模板... 设 f[i] 表示背包容积为 i 时所得的最大价值. 则状态转移方程为 f[j] = f[j - w[i]] + c[i] . #include<bits/std ...

  9. CF85B [Embassy Queue]

    Problem 题目简述 有 \(n\) 个人分别在 \(c_i\) 的时刻来,他们都要在 \(k_1\),\(k_2\) 和 \(k_3\) 窗口干不同的事,当有后面一人也排在在同一窗口时,必须等待 ...

  10. 一、docker的安装及docker-compose安装

    一. 安装docker 1.1安装 curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun # https://get.d ...