什么是事务

事务(Transaction)是由一系列对数据库中的数据进行访问与更新的操作所组成的一个程序执行单元。

在同一个事务中所进行的操作,要么都成功,要么就什么都不做。理想中的事务必须满足四大特性,这就是大名鼎鼎的ACID。

事务的ACID特性

并不是所有的事务都满足ACID特性,比如:对于Oracle和SQL Server数据库,其默认隔离级别是Read COMMITTED,就不满足I(隔离性)的要求;对于MySQL的NDB Cluster引擎来说,不满足D(持久性)的要求。

A(Atomicity)-原子性

原子性指的是数据库事务是不可分割的一部分,只有一个事务中的所有操作都成功,这个事务才算执行成功,一旦有一个操作失败,那么其他成功的操作也必须回滚。
以转账1000元场景为例,一个转账过程就是一个事务,这个事务主要包括以下两步:
1、从A账户扣除1000元
2、将B账户中增加1000元
试想,如果第一步成功了,那么第二步失败了,那就等于A的1000元钱直接消失了,相信这是任何人都不能接受的事项,所以数据库事务才需要保证原子性。

C(Consistent)-一致性

指的是在事务开始之前和事务结束之后,数据库的完整性约束都没有被破坏,事务执行的前后都是合法的数据状态。

比如我们有一张表中有一个字段name建立了一个唯一约束,那么当我们进行事务提交或者事务回滚之后,这个name必须依然保证唯一。

I(Isolation)-隔离性

隔离性就是说每个事务之间的操作应该相互隔离,互不干扰。比如说一个事务提交之前对另一个事务不可见。

隔离是一个相对抽象而复杂的概念,比如说事务之间的隔离性我们到底要隔离到哪种程度呢?所以,针对隔离,SQL92标准定义了4种隔离级别,这个放在后面事务的隔离级别中介绍。

D(Durable)-持久性

持久性这个概念就比较容易理解了,就是说事务一旦提交成功了,那么就应该是持久的,即使是数据库重启,服务器宕机等情况发生,数据都不会丢失(当然这个不能包括因为地震等自然灾害导致的存储数据的硬盘损发生不可逆的损坏)。

事务的管理

可能很多人会说自己都感知不到MySQL的事务,其实这是因为MySQL事务是默认开启了自动提交的,因此,如果要感知到事务,我们需要关闭自动提交或者显示开启事务。

事务的自动提交

查看自动提交语句:

SHOW VARIABLES LIKE 'autocommit';-- ON表示开启了自动提交
SELECT @@autocommit;-- 1表示开启了自动提交

执行如下语句关闭自动提交:

SET autocommit='OFF';
SET @@autocommit = 0;

不过需要注意的是,这种修改方式只是在当前会话窗口生效,对其他会话窗口是不生效的,MySQL几乎所有变量设置都会分成两个级别,session(会话)和global(全局)级别,默认就是session级别。

常用的事务控制语句

  • START TRANSACTION或者BEGIN:显示的开启事务。需要注意的是在存储过程中只能用START TRANSACTION开启事务,因为存储过程本来有BEGIN…END语法,两者会冲突。
  • COMMIT:提交事务。也可以写成COMMIT WORK。
  • ROLLBACK:回滚事务。也可以写成ROLLBACK WORK。
  • SAVEPOINT identifier:自定义保存点,适用于长事务,可以回滚到我们自定义的位置。
  • RELEASE SAVEPOINT identifier:删除一定保存点,如果没有保存点的时候,会报错
  • ROLLBACK TO[SAVEPOINT] identifier:回滚到指定保存点。

COMMIT和COMMIT WORK的区别

这两个都能提交一个事务,区别就在于提交事务之后的操作,同样的还有ROLLBACK和ROLLBACK WORK,主要是通过一个变量来控制:completion_type,可以执行下面的sql来查看结果:

SHOW VARIABLES LIKE '%completion_type%';

completion_type有如下三种结果:

举个栗子1:

SET completion_type=1; --1
begin;--2
INSERT test2 VALUES(1,'张1');--3
commit work;--4
INSERT test2 VALUES(2,'张1');--5
select * from test2;--6
rollback;--7
select * from test2;--8

第4条语句中,我们提交了一个事务,第5条语句中我们又插入了一条数据,此时第六条语句可以查询出2条数据,接下来我们回滚,语句8再去查询就会发现只剩一条数据了,因为语句6倍回滚了,我们在语句4之后并没有显示的开启一个事务,这就说明语句4自动开启了一个新的事务。

举个栗子2:

SET completion_type=2;
begin;
INSERT test2 VALUES(3,'张1');
commit work;
select * from test2;

最后一条语句返回如下结果:

先提示的断开连接,然后自动重连。测试这个例子的时候用工具比如sqlyog可能会不是很明显,因为工具会自动帮忙重连,看起来就好像没断开一样,建议用命令窗口的形式测试

事务的分类

从事务的理论角度来说,我们可以把事务分为以下五大类:

扁平事务

这种是最简单也是最常用的一种事务,这种事务中的所有操作都是原子的,要么全部成功,要么什么都不做。

带有保存点的扁平事务

这种一般比较适合于长事务,事务处理到后面报错的时候,我们可以选择不全部回滚事务,而是回滚到我们自定义好的某一个保存点。如下例子:

BEGIN;
INSERT test VALUES(1,'张1');
SAVEPOINT A
INSERT test VALUES(2,'张2');
ROLLBACK TO A
COMMIT;

上面示例语句中,我么你定义了一个保存点A,然后在后面又回滚到A,这时候提交事务,那么第二条插入语句是失败的,而第一条语句是成功的。

注意:回滚到指定保存点之后,事务仍然还在活动状态,我们依然需要执行COMMIT或者ROLLBACK语句才算结束了事务

链事务

在提交一个事务之后,释放掉我们不需要的数据,将必要的数据隐式的传给下一个事务。(注意:提交事务操作和开始下一个事务操作是一个原子操作)这就意味着下一个事务能看到上一个事务的结果。

链事务可以看成带有保存点的特殊事务,他们的区别就是带有保存点的事务可以回滚到任意保存点,但是回滚之后事务仍然活跃,需要执行COMMIT或者ROLLBACK之后才结束事务,而链事务中只能回滚到最近的一个保存点(即开始事务的点)。

链事务可以通过上面的completion_type参数来实现。上文中有举例使用方法,这里就不重复举例了。

嵌套事务

嵌套事务就是说一个事务之中嵌套另一个事务,事务之间存在父子关系,子事务的提交之后并不生效,需要等到父事务提交之后才会生效。

需要注意的是MySQL原生并不支持嵌套事务,但是可以通过保存点模拟嵌套事务,只是说这么模拟的话就没有真正的嵌套事务这么灵活。

分布式事务

分布式事务通常就是在分布式环境下,多个数据库下运行不同的扁平事务。多个数据库环境下运行的扁平事务就合成了一个分布式事务。

事务的隔离级别

Read Uncommitted(未提交读)

简称RU。这种是最低的隔离级别,等于没有隔离,基本上没有数据库会使用这个级别。一个事务可以读取到其他事务未提交的数据,这种也叫做脏读。

什么是脏读?请看下面这个例子:

左边是事务1,先查一次,查到id为1的数据name为张三,这时候事务2又来了,把张三改成了李四,然后事务1又进行了一次查询,查出来了name为李四,那么假如这时候事务2发生了回滚,也就是name还是张三,但是事务1却读到了李四,这就是脏读。

Read Committed(已提交读)

简称RC。一个事务只能读取到其他事务已提交的数据,就是说在一个事务里面,执行同样的查询,会出现两次不一样的结果。Oracle和SQL Server数据库默认的数据库隔离级别。这种隔离级别解决了脏读问题,但是会出现不可重复读的问题。

什么是不可重复读?还是看上面那个例子,假设事务2更新之后马上就提交,然后事务1第二次查询查出来的结果还是李四,只是这次就不算是脏读了,因为事务2提交了,这种就叫不可重复读,因为事务1中两次查询同一条数据结果不一样。

Repeatable Read(可重复读)

简称RR。这种隔离级别解决了不可重复读问题,就是说在同一个事务中,执行相同的查询,结果都是一样的,但是这种级别会出现幻读问题(InnoDB引擎例外,InnoDB引擎通过间隙锁解决了幻读问题)。

什么是幻读?请看下面这个例子:

上面图形中,事务1进行了一个范围查询,第一次只能查出一条记录,这时候事务2来插入了一条数据,然后事务1再次执行同一个查询,这时候就能查出来两条记录,也就是多了一条,给人一种幻觉,所以称之为幻读。

说到这里,可能有人就有疑问了,因为感觉不可重复读和幻读都是读取到已提交事务的结果,好像没什么区别?确实如此,不可重复读和幻读本质上是一样的,但是不可重复读针对的是更新和删除操作,而幻读仅针对插入操作。

Serializable(串行化)

这种是隔离的最高级别,也就是说所有的事务都是串行执行的,也就不存在并发事务,脏读,可重复读和幻读问题自然也就没有了。

不同隔离级别对比

不同的隔离级别可以解决不同的问题,大致如下图:

对于未提交读和已提交读大家可能都很好理解,只要控制一个事务提交之后才能对另一个事务可见,但是对于可重复读,MySQL到底是如何实现即使一个事务已经提交了,还能对另一个事务不可见呢?这就是接下来我们要讲解的MVCC了。

事务隔离的实现方案

事务隔离的实现方案有两种,LBCC和MVCC

LBCC

LBCC,基于锁的并发控制,英文全称Based Concurrency Control。这种方案比较简单粗暴,就是一个事务去读取一条数据的时候,就上锁,不允许其他事务来操作(当然这个锁的实现也比较重要,如果我们只锁定当前一条数据依然无法解决幻读问题)。

当前读

这个概念其实很好理解,MySQL加锁之后就是当前读。假如当前事务只是加共享锁,那么其他事务就不能有排他锁,也就是不能修改数据;而假如当前事务需要加排他锁,那么其他事务就不能持有任何锁。总而言之,能加锁成功,就确保了除了当前事务之外,其他事务不会对当前数据产生影响,所以自然而然的,当前事务读取到的数据就只能是最新的,而不会是快照数据(后文MVCC会解释快照读概念)。

LBCC方案中,如果我们的业务系统是读多写少的话,这种方案就会极大影响了效率,所以我们就有了另一种解决方案:MVCC。

MVCC

MVCC,多版本的并发控制,英文全称:Multi Version Concurrency Control。就是当我们在修改数据的时候,可以为这条数据创建一个快照,后面就可以直接读取这个快照。

那么MVCC具体到底是如何实现的呢?

为了实现MVCC机制,InnoDB内部为每一行添加了两个隐藏列:DB_TRX_ID和DB_ROLL_PTR(MySQL另外还有一个隐藏列DB_ROW_ID,这是在InnoDB表没有主键的时候会用来作为主键,想详细了解可以点击这里)。

DB_TRX_ID

长度为6字节,存储了插入或更新语句的最后一个事务的事务ID。

DB_ROLL_PTR

长度为7字节,称之为:回滚指针。回滚指针指向写入回滚段的undo log记录,读取记录的时候会根据指针去读取undo log中的记录。

正因为MySQL中undo log中会维护一个历史数据记录,所以我们应该养成定期提交事务的习惯,否则回滚段会越来越大,甚至占满了表空间。

快照读

快照读是针对上文的当前读而言,指的是在RR隔离级别下,在不加锁的情况下MySQL会根据回滚指针选择从undo log记录中获取快照数据,而不总是获取最新的数据,这也就是为什么另一个事务提交了数据,在当前事务中看到的依然是另一个事务提交之前的数据。

MySQL什么时候开始读取快照

我们先看看MySQL默认隔离级别RR下的一个例子(注意,test和test2两张表一开始都是空表,均只有id和name两个字段)。

  • 场景1(事务1操作数据之后再进行第一次查询):
  • 场景2(事务1不进行任何操作,事务2先开始第一次查询)

通过上面两个场景中我们可以得出结论:RR隔离级别快照并不是在BEGIN就开始产生了,而是要等到事务当中的第一次查询之后才会产生快照,之后的查询就只读取这个快照数据

  • 场景3(事务2先进行一次t1表查询之后,事务1再去操作其他表t2)

从场景3我们可以得出结论:RR隔离级别快照并不只是针对当前所查询的数据,而是针对当前MySQL中的所有数据(跨库也一样,只要在同一个MySQL)

MVCC查询机制

MVCC机制到底如何查询的呢?假设由很多个事务同时进行,那么就会产生很多快照,查询的时候又到底是怎么做的呢?

接下来我们把抽象的概念具体化,假定DB_TRX_ID和DB_ROLL_PTR均为整型,接下来我们进行查询演示:

1、清空原先的test表,事务A插入两条数据,此时DB_TRX_ID(事务id)为1,DB_ROLL_PTR(回滚指针为null)

2、这时候事务B进行了一次查询,会得到上面的结果,事务2还没提交的时候又来了事务C,事务C插入了id=3的数据,此时表中的数据如下:

注意,这时候第3条数据的事务id为3,因为事务2也会产生一个事务id
3、这时候事务B再次进行查询,根据上面了解的,我们知道,这时候应该是查询不出王五的,所以实际上二次查询可能是这么查的:

select * from test where 事务id<=2-- 因为当前的事务id为2

4、假如这时候事务D又来了,把id=1的数据给删除了,这时候会把原数据的回滚指针记录为当前的事务id:4,所以此时数据如下:

5、回到事务B,继续查询,应该还是只有1和2两条数据,那么他可能是这么查询的:

select * from test where 事务id<=2 and (回滚指针 is null or 回滚指针 >2)

6、假如这时候又来了事务E,对第2条数据进行了更新,这时候会生产一条事务id为5的数据,并把原数据的回滚指针也同时标记为当前的事务id:5,那么会得到如下数据:

根据上面猜测,执行下面的查询:

select * from test where 事务id<=2 and (回滚指针 is null or 回滚指针 >2)

这时候发现,查出来的数据还是只有1和2两条。

MVCC查询两大规则

综上,MVCC大致查询规则如下:
1、只查询事务id小于等于当前事务id的数据。(这里要等于是因为假如自己的事务插入了一条数据,会生成一条当前事务id的数据,所以必须包含本事务自己插入的数据)
2、只查询未删除(回滚指针为空)或者回滚指针大于当前事务id的数据。(这里不能等于是因为假如自己的事务删除了一条数据,会生成数据的回滚指针为当前事务id,所以必须排除掉自己删除的数据)

当然,上面规则只是简化了,实际查询远比这里复杂,只是希望借助这种简单化的概念可以帮助大家更好的理解MVCC工作机制。

作者:双子孤狼
原文链接:https://blog.csdn.net/zwx900102/article/details/106544843

limit实现原理 mysql_解读数据库:深入分析MySQL中事务以及MVCC的实现原理相关推荐

  1. 【MySQL系列5】深入分析MySQL中锁并详解锁解决幻读问题

    MySQL锁分析 MySQL系列文章汇总 前言 什么是锁 锁的分类 全局锁 表锁 行锁 共享锁 排他锁 意向锁 各种锁的兼容关系 锁到底锁的是什么 举例猜测 结论 行锁的算法 记录锁(Record L ...

  2. mysql中decimal不能为空吗_程序员,知道Mysql中事务ACID的原理吗?

    点击上方"linkoffer", 选择关注公众号高薪职位第一时间送达 引言 照例,我们先来一个场景~ 面试官:"知道事务的四大特性么?" 你:"懂,A ...

  3. mysql 事物的持久性是指_详解MySQL中事务的持久性实现原理

    前言 说到数据库事务,大家脑子里一定很容易蹦出一堆事务的相关知识,如事务的ACID特性,隔离级别,解决的问题(脏读,不可重复读,幻读)等等,但是可能很少有人真正的清楚事务的这些特性又是怎么实现的,为什 ...

  4. MySQL中事务的持久性实现原理

    前言 说到数据库事务,大家脑子里一定很容易蹦出一堆事务的相关知识,如事务的ACID特性,隔离级别,解决的问题(脏读,不可重复读,幻读)等等,但是可能很少有人真正的清楚事务的这些特性又是怎么实现的,为什 ...

  5. mysql 中 RC、RR隔离级别的原理及区别

    今天分享 mysql 中 RC.RR隔离级别的原理及区别: 1.首先简介mysql四种隔离级别: 1)未提交读(READ UNCOMMITED)脏读 2) 已提交读 (READ COMMITED)简称 ...

  6. 不同服务器数据库表连接查询修改,如何连接多个数据库,mysql中的服务器和查询两个表中的对方?...

    我期待从不同服务器连接两个不同的数据库.此外,我想运行一个查询,从两个数据库中获取数据到一个单一的结果.我正在使用mysql在PHP脚本中执行此操作.这里是如何很期待做[没有成功:)]如何连接多个数据 ...

  7. mysql增加sort_buffer_MySQL数据库之MySQL中的sort_buffer_size参数大小的设置问题

    本文主要向大家介绍了MySQL数据库之MySQL中的sort_buffer_size参数大小的设置问题 ,通过具体的内容向大家展现,希望对大家学习MySQL数据库有所帮助. 看到sort_buffer ...

  8. mysql数据库算法_数据库:MySQL索引背后的数据结构及算法原理【转】

    原文:http://blog.codinglabs.org/articles/theory-of-mysql-index.html 摘要 本文以MySQL数据库为研究对象,讨论与数据库索引相关的一些话 ...

  9. 患者数据库mysql_关系型数据库之MySQL基础总结_part1

    一:数据库的操作语言的种类 MySQL 是我们最常使用的关系型数据库,对于MySQL的操作的语言种类又可以分为:DDL,DML,DCL,DQL DDL:是数据库的定义语言:主要对于数据库信息的一些定义 ...

最新文章

  1. Oracle 聚合函数(Aggregate Functions)说明
  2. java 两个stream合并_Java Stream 流如何进行合并操作
  3. win7部分便笺的元数据已被损坏
  4. Flask-admin 学习及一些笔记
  5. C# new关键字和对象类型转换(双括号、is操作符、as操作符)
  6. [设计模式-行为型]解释器模式(Interpreter)
  7. c语言用户给顺序表输入值,C语言与数据结构实验指导(删减程序版)课案.docx
  8. 从零开始打造自己的PHP框架——第3章
  9. 大一c语言常见编程题,大一c语言考试复习题
  10. 【联想拯救者R7000】蓝牙和wifi图标不显示问题(已解决)
  11. PowerDesigner使用教程 —— 概念数据模型(CDM模型)
  12. 方程检验格式图片_Excel绘制标准曲线全图片教程
  13. opencv Mat 16位unsigned数据显示为黑色
  14. Pseudo-伪标签
  15. 内盘外盘哪个比较好?如何分析
  16. FineReport缺失字体检测
  17. PyQt5 QLabel改变字体和设置背景图片
  18. 未封装的扩展程序是什么意思_晶圆级封装是什么意思?
  19. xposed android debug,Android 手机开启全局调试xposed插件
  20. Android传感器的使用(1)——摇一摇切换图片

热门文章

  1. react native仿微信性别选择-自定义弹出框
  2. Nginx For Windows 关于 worker_connections 不生效问题
  3. linux——客户端服务器文件传输
  4. CentOS7搭建部署Ambari 2.6.2.0最新版(HDP-UTILS、HDP-GPL)大数据平台
  5. 【Jetson-Nano】2.Tensorflow和Pytorch的安装
  6. 陕西省2021年高考成绩结果查询,陕西招生考试信息网:2021年陕西高考成绩查询入口、查分系统...
  7. win10雷电3接口驱动_技嘉推出B550 主板首发雷电3接口:40Gbps速率、Intel主控
  8. ASP.NET MVC 使用Log4Net记录系统运行中问题
  9. java hs err pid_JAVA 奔溃 生成hs_err_pid****的文件,求大神看看
  10. 如何在QQ浏览器查看默认搜索引擎