mysql事务篇:acid原则事务隔离级别及事务机制原理剖析

author author     2022-12-04     708

关键词:

引言

众所周知,​​MySQL​​数据库的核心功能就是存储数据,通常是整个业务系统中最重要的一层,可谓是整个系统的“大本营”,因此只要​​MySQL​​存在些许隐患问题,对于整个系统而言都是致命的。那此刻不妨思考一个问题:

​MySQL​​在接受外部数据写入时,有没有可能会发生问题呢?

有人也许会笑着回答:“那怎么可能啊,​​MySQL​​在写入数据时怎么会存在问题呢”。

的确,​​MySQL​​本身在写入数据时并不会有问题,就算部署​​MySQL​​的机器断电/宕机,其内部也有一套健全的机制确保数据不丢失。但往往风险并不来自于表象,虽然​​MySQL​​写入数据没问题,但结合业务来看就会有一个很大的隐患,此话怎讲呐?先看案例:

-- 从库存表中扣减商品数量
UPDATE `zz_inventory` SET ......;

-- 向订单表、订单详情表中插入订单记录
INSERT INTO `zz_order` VALUES(....);
INSERT INTO `zz_order_info` VALUES(....);

-- 向物流表中插入相应的物流信息
INSERT INTO `zz_logistics` VALUES(....);

上述的伪​​SQL​​中,描述的是一个经典下单业务,先扣库存数量、再增加订单记录、再插入物流信息,按照正常的逻辑来看,上面的​​SQL​​也没有问题。但是请仔细想想!实际的项目中,这三组​​SQL​​是会由客户端(​​Java​​线程)一条条发过来的,假设执行到「增加订单记录」时,​​Java​​程序那边抛出了异常,会出现什么问题呢?

乍一想似乎没问题,但仔细一想:Java线程执行时出现异常会导致线程执行中断。

因为​​Java​​线程中断了,所以线程不会再向数据库发送「增加订单详情记录、插入物流信息」的​​SQL​​,此刻再来想想这个场景,由于增加订单详情和物流信息的​​SQL​​都未发送过来,因此必然也不会执行,但此时库存已经扣了,用户钱也付了,但却没有订单和物流信息,这引发的后果估计老板都能杀个程序员祭天了......

其实上面列举的这个案例,在数据库中被称之为事务问题,接下来一起聊一聊。

一、事务的ACID原则

什么是事务呢?事务通常是由一个或一组​​SQL​​组成的,组成一个事务的​​SQL​​一般都是一个业务操作,例如前面聊到的下单:「扣库存数量、增加订单详情记录、插入物流信息」,这一组​​SQL​​就可以组成一个事务。

而数据库的事务一般也要求满足​​ACID​​原则,​​ACID​​是关系型数据库实现事务机制时必须要遵守的原则。

​ACID​​主要涵盖四条原则,即:

  • ​A/Atomicity​​:原子性
  • ​C/Consistency​​:一致性
  • ​I/Isolation​​:独立性/隔离性
  • ​D/Durability​​:持久性

那这四条原则分别是什么意思呢?接下来一起聊一聊。

1.1、Atomicity原子性

原子性这个概念,在​​MySQL​​中原子性的含义也大致相同,指组成一个事务的一组​​SQL​​要么全部执行成功,要么全部执行失败,事务中的一组​​SQL​​会被看成一个不可分割的整体,当成一个操作看待。

好比事务​​A​​由​​①、②、③​​条​​SQL​​组成,那这一个事务中的三条​​SQL​​必须全部执行成功,只要其中任意一条执行失败,例如​​②​​执行时出现异常了,此时就会导致事务​​A​​中的所有操作全部失败。

1.2、Consistency一致性

一致性也比较好理解,也就是不管事务发生的前后,​​MySQL​​中原本的数据变化都是一致的,也就是​​DB​​中的数据只允许从一个一致性状态变化为另一个一致性状态。这句话似乎听起来有些绕,不太好理解对嘛?简单解释一下就是:一个事务中的所有操作,要么一起改变数据库中的数据,要么都不改变,对于其他事务而言,数据的变化是一致的,上栗子:

假设此时有一个事务​​A​​,这个事务隶属于一个下单操作,由「⓵扣库存数量、⓶增加订单详情记录、⓷插入物流信息」三这条​​SQL​​操作组成。

一致性的含义是指:在这个事务执行前,数据库中的数据是处于一致性状态的,而​​SQL​​执行完成之后事务提交,数据库中的数据依旧处于一个“一致性”状态,也就是库存数量+订单数量永远是等于最初的库存总数的,比如原本的总库存是​​10000​​个,此时库存剩余​​8888​​个,那也就代表着必须要有​​1112​​条订单数据才行。

这也就是前面说的:“事务发生的前后,​​MySQL​​中原本的数据变化都是一致的”,这句话的含义,不可能库存减了,但订单没有增加,这样就会导致数据库整体数据出现不一致。

如果出现库存减了,但订单没有增加的情况,就代表着事务执行过程中出现了异常,此时​​MySQL​​就会利用事务回滚机制,将之前减的库存再加回去,确保数据的一致性。

但来思考一个问题,如果事务执行过程中,刚减完库存后,​​MySQL​​所在的服务器断电了咋整?似乎无法利用事务回滚机制去确保数据一致性了撒?对于这点大可不必担心,因为​​MySQL​​宕机重启后,会通过分析日志的方式恢复数据,确保一致性(对于这点稍后再细聊)。

1.3、Isolation独立性/隔离性

简单理解原子性和一致性后,再来看看​​ACID​​中的隔离性,在有些地方也称之为独立性,意思就是指多个事务之间都是独立的,相当于每个事务都被装在一个箱子中,每个箱子之间都是隔开的,相互之间并不影响,同样上个栗子:

假设数据库的库存表中,库存数量剩余​​8888​​个,此时有​​A、B​​两个并发事务,这两个事务都是相同的下单操作,由「⓵扣库存数量、增⓶加订单详情记录、⓷插入物流信息」三这条​​SQL​​操作组成。

此时​​A、B​​两个事务一起执行,同一时刻执行减库存的​​SQL​​,因此这里是并发执行的,那两个事务之间是否会互相影响,导致扣的是同一个库存呢?答案是不会,​​ACID​​原则中的隔离性保障了并发事务的顺序执行,一个未完成事务不会影响另外一个未完成事务。

隔离性在底层是如何实现的呢?基于​​MySQL​​的锁机制和​​MVCC​​机制做到的(后续《MySQL事务与锁原理篇》再详细去讲)。

1.4、Durability持久性

相较于之前的原子性、一致性、隔离性来说,持久性是​​ACID​​原则中最容易理解的一条,持久性是指一个事务一旦被提交,它会保持永久性,所更改的数据都会被写入到磁盘做持久化处理,就算​​MySQL​​宕机也不会影响数据改变,因为宕机后也可以通过日志恢复数据。

也就相当于你许下一个诺言之后,那你无论遇到什么情况都会保证做到,就算遇到山水洪灾、地球毁灭、宇宙爆炸.....任何情况也好,你都会保证完成你的诺言为止。

二、MySQL的事务机制综述

刚刚说到的​​ACID​​原则是数据库事务的四个特性,也可以理解为实现事务的基础理论,那接下来一起看看​​MySQL​​所提供的事务机制。在​​MySQL​​默认情况下,一条​​SQL​​会被视为一个单独的事务,同时也无需咱们手动提交,因为默认是开启事务自动提交机制的,如若你想要将多条​​SQL​​组成一个事务执行,那需要显式的通过一些事务指令来实现。

2.1、手动管理事务

在​​MySQL​​中,提供了一系列事务相关的命令,如下:

  • ​start transaction | begin | begin work​​:开启一个事务
  • ​commit​​:提交一个事务
  • ​rollback​​:回滚一个事务

当需要使用事务时,可以先通过​​start transaction​​命令开启一个事务,如下:

-- 开启一个事务
start transaction;

-- 第一条SQL语句
-- 第二条SQL语句
-- 第三条SQL语句

-- 提交或回滚事务
commit || rollback;

对于上述​​MySQL​​手动开启事务的方式,相信大家都不陌生,但大家有一点应该会存在些许疑惑:事务是基于当前数据库连接而言的,而不是基于表,一个事务可以由操作不同表的多条​​SQL​​组成,这句话什么意思呢?看下图:

MySQL事务篇:ACID原则、事务隔离级别及事务机制原理剖析_sql

上面画出了两个数据库连接,假设连接​​A​​中开启了一个事务,那后续过来的所有​​SQL​​都会被加入到一个事务中,也就是图中连接​​A​​,后面的​​SQL②、SQL③、SQL④、SQL⑤​​这五条都会被加入到一个事务中,只要在未曾收到​​commit/rollback​​命令之前,这个连接来的所有​​SQL​​都会加入到同一个事务中,因此对于这点要牢记,开启事务后一定要做提交或回滚处理。

不过在连接​​A​​中开启事务,是不会影响连接​​B​​的,这也是我说的:事务是基于当前数据库连接的,每个连接之间的事务是具备隔离性的,比如上个真实栗子~

此时先打开两个​​cmd​​命令行,然后用命令连接​​MySQL​​,或者也可以用​​Navicat、SQLyog​​等数据库可视化工具,新建两个查询,如下:

MySQL事务篇:ACID原则、事务隔离级别及事务机制原理剖析_mysql_02

这里插个小偏门知识:当你在Navicat、SQLyog​这类可视化工具中,新建一个查询时,本质上它就是给你建立了一个数据库连接,每一个新查询都是一个新的连接。

然后开始在两个查询中编写对应的​​SQL​​命令,先在查询窗口​​①​​中开启一个事务:

-- 先查询一次表数据
SELECT * FROM `zz_users`;
+---------+-----------+----------+----------+---------------------+
| user_id | user_name | user_sex | password | register_time |
+---------+-----------+----------+----------+---------------------+
| 1 | 熊猫 | 女 | 6666 | 2022-08-14 15:22:01 |
| 2 | 竹子 | 男 | 1234 | 2022-09-14 16:17:44 |
| 3 | 子竹 | 男 | 4321 | 2022-09-16 07:42:21 |
| 4 | 1111 | 男 | 8888 | 2022-09-17 23:48:29 |
+---------+-----------+----------+----------+---------------------+

-- 开启事务
start transaction;

-- 修改 ID=4 的姓名为:黑熊
update `zz_users` set `user_name` = "黑熊" where `user_id` = 4;

-- 删除 ID=1 的行数据
delete from `zz_users` where `user_id` = 1;

-- 再次查询一次数据
SELECT * FROM `zz_users`;
+---------+-----------+----------+----------+---------------------+
| user_id | user_name | user_sex | password | register_time |
+---------+-----------+----------+----------+---------------------+
| 2 | 竹子 | 男 | 1234 | 2022-09-14 16:17:44 |
| 3 | 子竹 | 男 | 4321 | 2022-09-16 07:42:21 |
| 4 | 黑熊 | 男 | 8888 | 2022-09-17 23:48:29 |
+---------+-----------+----------+----------+---------------------+

观察上面的结果,对比开启事务前后的的表数据查询,在事务中分别修改、删除一条数据后,再次查询表数据时会观察到表数据已经变化,此时再去查询窗口​​②​​中查询表数据:

SELECT * FROM `zz_users`;
+---------+-----------+----------+----------+---------------------+
| user_id | user_name | user_sex | password | register_time |
+---------+-----------+----------+----------+---------------------+
| 1 | 熊猫 | 女 | 6666 | 2022-08-14 15:22:01 |
| 2 | 竹子 | 男 | 1234 | 2022-09-14 16:17:44 |
| 3 | 子竹 | 男 | 4321 | 2022-09-16 07:42:21 |
| 4 | 1111 | 男 | 8888 | 2022-09-17 23:48:29 |
+---------+-----------+----------+----------+---------------------+

在查询窗口​​②​​中,也就相当于在第二个连接中查询数据时,会发现第一个连接(窗口​​①​​)改变的数据并未影响到第二个连接,啥原因呢?这是因为窗口​​①​​中还未提交事务,所以第一个连接改变的数据不会影响第二个连接。

其实具体的原因是由于​​MySQL​​事务的隔离机制造成的,但对于这点后续再去分析。

此时在查询窗口​​①​​中,输入​​rollback​​命令,让当前事务回滚:

-- 回滚当前连接中的事务
rollback;
-- 再次查询表数据
SELECT * FROM `zz_users`;
+---------+-----------+----------+----------+---------------------+
| user_id | user_name | user_sex | password | register_time |
+---------+-----------+----------+----------+---------------------+
| 1 | 熊猫 | 女 | 6666 | 2022-08-14 15:22:01 |
| 2 | 竹子 | 男 | 1234 | 2022-09-14 16:17:44 |
| 3 | 子竹 | 男 | 4321 | 2022-09-16 07:42:21 |
| 4 | 1111 | 男 | 8888 | 2022-09-17 23:48:29 |
+---------+-----------+----------+----------+---------------------+

结果很明显,当事务回滚后,之前所做的数据更改操作全部都会撤销,恢复到事务开启前的表数据。当然,如果不手动开启事务,执行下述这条​​SQL​​会发生什么情况呢?

update `zz_users` set `user_name` = "黑熊" where `user_id` = 4;

会直接修改表数据,并且其他连接可见,因为​​MySQL​​默认将一条​​SQL​​视为单个事务,同时默认开启自动提交事务,也就是上面这条​​SQL​​执行完了之后就会自动提交。

-- 查看 自动提交事务 是否开启
SHOW VARIABLES LIKE autocommit;

-- 关闭或开启自动提交
SET autocommit = 0|1|ON|OFF;
复制代码

上述的​​[0/ON]​​是相同的意思,表示开启自动提交,​​[1/OFF]​​则表示关闭自动提交。

2.2、事务回滚点

在上面简单阐述了事务的基本使用,但假设目前有一个事务,由很多条​​SQL​​组成,但是我想让其中一部分执行成功后,就算后续​​SQL​​执行失败也照样提交,这样可以做到吗?从前面的理论上来看,一个事务要么全部执行成功,要么全部执行失败,似乎做不到啊,但实际上是可以做到的,这里需要利用事务的回滚点机制。

在某些​​SQL​​执行成功后,但后续的操作有可能成功也有可能失败,但不管成功亦或失败,你都想让前面已经成功的操作生效时,此时就可在当前成功的位置设置一个回滚点。当后续操作执行失败时,就会回滚到该位置,而不是回滚整个事务中的所有操作,这个机制则称之为事务回滚点。

在​​MySQL​​中提供了两个关于事务回滚点的命令:

  • ​savepoint point_name​​:添加一个事务回滚点
  • ​rollback to point_name​​:回滚到指定的事务回滚点

以前面的案例来演示效果,如下:

-- 先查询一次用户表
SELECT * FROM `zz_users`;
-- 开启事务
start transaction;
-- 修改 ID=4 的姓名为:黑熊
update `zz_users` set `user_name` = "黑熊" where `user_id` = 4;
-- 添加一个事务回滚点:update_name
savepoint update_name;
-- 删除 ID=1 的行数据
delete from `zz_users` where `user_id` = 1;
-- 回滚到 update_name 这个事务点
rollback to update_name;
-- 再次查询一次数据
SELECT * FROM `zz_users`;
-- 提交事务
COMMIT;

上述代码中开启了一个事务,事务中总共修改和删除两条​​SQL​​组成,然后在修改语句后面添加了一个事务回滚点​​update_name​​,在删除语句后回滚到了前面添加的回滚点。

但要注意:回滚到事务点后不代表着事务结束了,只是事务内发生了一次回滚,如果要结束当前这个事务,还依旧需要通过​​commit|rollback;​​命令处理。

其实借助事务回滚点,可以很好的实现失败重试,比如对事务中的每个​​SQL​​添加一个回滚点,当执行一条​​SQL​​时失败了,就回滚到上一条​​SQL​​的事务点,接着再次执行失败的​​SQL​​,反复执行到所有​​SQL​​成功为止,最后再提交整个事务。

当然,这个只是理论上的假设,实际业务中不要这么干~

2.3、MySQL事务的隔离机制

OK~,在前面做的小测试中,咱们会发现不同的数据库连接中,一个连接的事务并不会影响其他连接,当时也稍微的提过一嘴:这是基于事务隔离机制实现的,那接下来重点聊一聊​​MySQL​​的事务隔离机制。其实在​​MySQL​​中,事务隔离机制分为了四个级别:

  • ①​​Read uncommitted/RU​​:读未提交
  • ②​​Read committed/RC​​:读已提交
  • ③​​Repeatable read/RR​​:可重复读
  • ④​​Serializable​​:序列化/串行化

上述四个级别,越靠后并发控制度越高,也就是在多线程并发操作的情况下,出现问题的几率越小,但对应的也性能越差,​​MySQL​​的事务隔离级别,默认为第三级别:​​Repeatable read​​可重复读,但如若想要真正理解这几个隔离级别,得先明白几个因为并发操作造成的问题。

2.3.1、脏读、幻读、不可重复读问题
数据库的脏读问题

首先来看看脏读,脏读的意思是指一个事务读到了其他事务还未提交的数据,也就是当前事务读到的数据,由于还未提交,因此有可能会回滚,如下:

MySQL事务篇:ACID原则、事务隔离级别及事务机制原理剖析_sql_03

比如上图中,​​DB​​连接①/事务​​A​​正在执行下单业务,目前扣减库存、增加订单两条​​SQL​​已经完成了,恰巧此时​​DB​​连接②/事务​​B​​跑过来读取了一下库存剩余数量,就将事务​​A​​已经扣减之后的库存数量读回去了。但好巧不巧,事务​​A​​在添加物流信息时,执行异常导致事务​​A​​全部回滚,也就是原本扣的库存又会增加回去。

在个案例中,事务​​A​​先扣减了库存,然后事务回滚时又加了回去,但连接②已经将扣减后的库存数量读回去操作了,这个过程就被称为数据库脏读问题。这个问题很严重,会导致整个业务系统出现问题,数据最终错乱。

数据库的不可重复读问题

再来看看不可重复读问题,不可重复读问题是指在一个事务中,多次读取同一数据,先后读取到的数据不一致,如下:

MySQL事务篇:ACID原则、事务隔离级别及事务机制原理剖析_数据_04

你没看错,就是对前面那张图稍微做了一点改造,事务​​A​​执行下单业务时,因为添加物流信息的时候出错了,导致整个事务回滚,事务回滚完成后,事务​​A​​就结束了。但事务​​B​​却并未结束,在事务​​B​​中,在事务​​A​​执行时读取了一次剩余库存,然后在事务回滚后又读取了一次剩余库存,仔细想想:​​B​​事务第一次读到的剩余库存是扣减之后的,第二次读到的剩余库存则是扣减之前的(因为​​A​​事务回滚又加回去了)。

在上述这个案例中,同一个事务中读取同一数据,结果却并不一致,也就说明了该数据存在不可重复读问题,这样说似乎有些绕,那再结合可重复读来一起理解: 可重复读的意思是:在同一事务中,不管读取多少次,读到的数据都是相同的。

结合上述可重复读的定义,再去理解不可重复读问题会容易很多,重点是理解可重复、不可重复这个词义,为了更形象化一点,举个生活中的案例:

一张卫生纸,我先拿去擦了一下桌子上的污水渍,然后又放回了原位,当我想上厕所再次拿起时,它已经无法使用了,这就代表着一张卫生纸是不可重复使用的。

一个大铁锤,我先拿去敲一下松掉的桌腿,然后放回了原位,当我又想敲一下墙上的钉子再次拿起时,这个大铁锤是没有发生任何变化的,可以再次用来敲钉子,这就代表大铁锤是可以重复使用的。

相信结合这两个栗子,更能让你明白可重复与不可重复的概念定义。

数据库的幻读问题

对于幻读的解释在网上也有很多资料,但大部分资料是这样描述幻读问题的:

幻读:指同一个事务内多次查询返回的结果集不一样。比如同一个事务​​A​​,在第一次查询表的数据行数时,发现表中有​​n​​条行记录,但是第二次以同等条件查询时,却发现有​​n+1​​条记录,这就好像产生了幻觉。

这个说法实际上并不严谨,第一次读和第二次读同一数据,结果集并不相同,这其实属于一个不可重复读的问题,而并非幻读问题。那接下来举例说明一下什么叫做真正的幻读问题,先上图:

MySQL事务篇:ACID原则、事务隔离级别及事务机制原理剖析_sql_05

做过电商业务的小伙伴都清楚,一般用户购买商品后付的钱会先冻结在平台上,然后由平台在固定的时间内结算用户款,例如七天一结算、半月一结算等方式,在结算业务中通常都会涉及到核销处理,也就是将所有为「已签收状态」的订单改为「已核销状态」。

此时假设连接①/事务​​A​​正在执行「半月结算」这个工作,那首先会读取订单表中所有状态为「已签收」的订单,并将其更改为「已核销」状态,然后将用户款打给商家。

但此时恰巧,某个用户的订单正好到了自动确认收货的时间,因此在事务​​A​​刚刚改完表中订单的状态时,事务​​B​​又向表中插入了一条「已签收状态」的订单并提交了,当事务​​A​​完成打款后,再次查询订单表,结果会发现表中还有一条「已签收状态」的订单数据未结算,这就好像产生了幻觉一样,这才是真正的幻读问题。

当然,这样讲似乎还不是那么令人理解,再举个更通俗易懂的栗子,假设此时平台要升级,用户表中的性别字段,原本是以「男、女」的形式保存数据,现在平台升级后要求改为「​​0、1​​」代替。 因此事务​​A​​开始更改表中所有数据的性别字段,当负责执行事务​​A​​的线程正在更改最后一条表数据时,此时事务​​B​​来了,正好向用户表中插入了一条「性别=男」的数据并提交了,然后事务​​A​​改完原本的最后一条数据后,当再次去查询用户表时,结果会发现表中依旧还存在一条「性别=男」的数据,似乎又跟产生了幻觉一样。

经过上述这两个案例,大家应该能够理解真正的幻读问题,发生幻读问题的原因是在于:另外一个事务在第一个事务要处理的目标数据范围之内新增了数据,然后先于第一个事务提交造成的问题。

数据库脏写问题

其实除开三个读的问题外,还有有一个叫做脏写的问题,也就是多个事务一起操作同一条数据,例如两个事务同时向表中添加一条​​ID=88​​的数据,此时就会造成数据覆盖,或者主键冲突的问题,这个问题也被称之为更新丢失问题。

2.3.2、事务的四大隔离级别

在上面连续讲了脏读、不可重复读以及幻读三个问题,那这些问题该怎么解决呢?其实四个事务隔离级别,解决的实际问题就是这三个,因此一起来看看各级别分别解决了什么问题:

  • ①读未提交:处于该隔离级别的数据库,脏读、不可重复读、幻读问题都有可能发生。
  • ②读已提交:处于该隔离级别的数据库,解决了脏读问题,不可重复读、幻读问题依旧存在。
  • ③可重复读:处于该隔离级别的数据库,解决了脏读、不可重复读问题,幻读问题依旧存在。
  • ④序列化/串行化:处于该隔离级别的数据库,解决了脏读、不可重复读、幻读问题都不存在。

前面提到过,​​MySQL​​默认是处于第三级别的,可以通过如下命令查看目前数据库的隔离级别:

-- 查询方式①
SELECT @@tx_isolation;
-- 查询方式②
show variables like %tx_isolation%;
+---------------+-----------------+
| Variable_name | Value |
+---------------+-----------------+
| tx_isolation | REPEATABLE-READ |
+---------------+-----------------+
复制代码

其实数据库不同的事务隔离级别,是基于不同类型、不同粒度的锁实现的,因此想要真正搞懂隔离机制,还需要弄明白​​MySQL​​的锁机制,事务与锁机制二者之间本身就是相辅相成的关系,锁就是为了解决并发事务的一些问题而存在的,但对于锁的内容在后续的《MySQL锁篇》再细聊,这里就简单概述一下。

这里先说明一点,事务是基于数据库连接的,数据库连接在《MySQL架构篇》中曾说过:数据库连接本身会有一条工作线程来维护,也就是说事务的执行本质上就是工作线程在执行,因此所谓的并发事务也就是指多条线程并发执行。

多线程其实是咱们的老朋友了,在之前的《并发编程系列》中,几乎将多线程的底裤都翻出来了,因此结合多线程角度来看,脏读、不可重复读、幻读这一系列问题,本质上就是一些线程安全问题,因此需要通过锁来解决,而根据锁的粒度、类型,又分出了不同的事务隔离级别。

读未提交级别

这种隔离级别是基于「写互斥锁」实现的,当一个事务开始写某一个数据时,另外一个事务也来操作同一个数据,此时为了防止出现问题则需要先获取锁资源,只有获取到锁的事务,才允许对数据进行写操作,同时获取到锁的事务具备排他性/互斥性,也就是其他线程无法再操作这个数据。

但虽然这个级别中,写同一数据时会互斥,但读操作却并不是互斥的,也就是当一个事务在写某个数据时,就算没有提交事务,其他事务来读取该数据时,也可以读到未提交的数据,因此就会导致脏读、不可重复读、幻读一系列问题出现。

但是由于在这个隔离级别中加了「写互斥锁」,因此不会存在多个事务同时操作同一数据的情况,因此这个级别中解决了前面说到的脏写问题。

读已提交级别

在这个隔离级别中,对于写操作同样会使用「写互斥锁」,也就是两个事务操作同一事务时,会出现排他性,而对于读操作则使用了一种名为​​MVCC​​多版本并发控制的技术处理,也就是有事务中的​​SQL​​需要读取当前事务正在操作的数据时,​​MVCC​​机制不会让另一个事务读取正在修改的数据,而是读取上一次提交的数据(也就是读原本的老数据)。

也就是在这个隔离级别中,基于同一条数据而言,对于写操作会具备排他性,对于读操作则只能读已提交事务的数据,不会读取正在操作但还未提交的事务数据,为了理解还是简单的说一下其过程,同样有两个事务​​A、B​​。

事务​​A​​的主要工作是负责更新​​ID=1​​的这条数据,事务​​B​​中则是读取​​ID=1​​的这条数据。 此时当​​A​​正在更新数据但还未提交时,事务​​B​​开始读取数据,此时​​MVCC​​机制则会基于表数据的快照创建一个​​ReadView​​,然后读取原本表中上一次提交的老数据。然后等事务​​A​​提交之后,事务​​B​​再次读取数据,此时​​MVCC​​机制又会创建一个新的​​ReadView​​,然后读取到最新的已提交的数据,此时事务​​B​​中两次读到的数据并不一致,因此出现了不可重复读问题。

当然,对于MVCC事务及事务隔离级别

什么是事务事务是访问数据库的一个操作序列,数据库应用系统通过事务集来完成对数据库的存取。事务的正确执行使得数据库从一种状态转换为另一种状态。事务必须服从ISO/IEC所制定的ACID原则。ACID是原子性(atomicity)、一致... 查看详情

事务及事务隔离级别

什么是事务事务是访问数据库的一个操作序列,数据库应用系统通过事务集来完成对数据库的存取。事务的正确执行使得数据库从一种状态转换为另一种状态。事务必须服从ISO/IEC所制定的ACID原则。ACID是原子性(atomicity&#... 查看详情

转事务及事务隔离级别

什么是事务事务是访问数据库的一个操作序列,数据库应用系统通过事务集来完成对数据库的存取。事务的正确执行使得数据库从一种状态转换为另一种状态。事务必须服从ISO/IEC所制定的ACID原则。ACID是原子性(atomicity)、一致... 查看详情

mysql事物及隔离级别(代码片段)

阅读目录一、事务的基本要素(ACID)二、事务的四种隔离级别三、事务的并发问题四、MVCC在MySQL的InnoDB中的实现五MySQL死锁六EXPLAIN分析语句七锁的定义一、事务的基本要素(ACID)1、原子性(Atomicity)࿱... 查看详情

事务的特性及事务的隔离级别(转)

...ttp://www.cnblogs.com/fjdingsd/p/5273008.html  本篇讲诉数据库中事务的四大特性(ACID),并且将会详细地说明事务的隔离级别。  如果一个数据库声称支持事务的操作,那么该数据库必须要具备以下四个特性:⑴原子性(Atomicity)... 查看详情

事务的特性及事务的隔离级别(转)

...ttp://www.cnblogs.com/fjdingsd/p/5273008.html  本篇讲诉数据库中事务的四大特性(ACID),并且将会详细地说明事务的隔离级别。  如果一个数据库声称支持事务的操作,那么该数据库必须要具备以下四个特性:⑴原子性(Atomicity)... 查看详情

mysql事务的隔离级别和acid

...;会产生脏读,不可重复读,幻读)在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也被称之为脏读(DirtyRead)。read-c... 查看详情

数据库事务的四大特性及事务隔离级别

...www.cnblogs.com/fjdingsd/p/5273008.html   本篇讲诉数据库中事务的四大特性(ACID),并且将会详细地说明事务的隔离级别。  如果一个数据库声称支持事务的操作,那么该数据库必须要具备以下四个特性:⑴原子性(Atomicity)... 查看详情

mysql事务grant权限

事务mysql课堂笔记一、创建事务事务相当于将多个sql语句打包起来mysql事务四个原则ACID原则1.原子性要么都执行要么都不执行,一个事物就是一个整体2.一致性事物执行,前后数据保持一致,不存在数据丢失。3.隔离性在执行过程... 查看详情

mysql的事务和引擎,注意细品(代码片段)

mysql事务和引擎一、MySQL事务(一)、MySQL事务的概念(二)、事务的ACID特点1、ACID特点2、数据不一致产生的结果:(三)、事务的隔离1、MySQL事物隔离级别1.1查询全局事务隔离级别1.2查询会话事务隔离级别1.3设... 查看详情

mysql--新手必备sql基础知识事务acid及隔离级别(代码片段)

❤️‍您好,我是贾斯汀,本文主要分享数据库的一些基础知识!❤️‍SQL什么是SQL?【百度百科】结构化查询语言(StructuredQueryLanguage)简称SQL,是一种特殊目的的编程语言,是一种数据库查询和... 查看详情

mysql索引事务及存储引擎(代码片段)

MySQL索引、事务及存储引擎MySQL索引索引的概念索引的作用创建索引的原则依据索引的分类和创建普通索引(最基本的索引)唯一索引主键索引组合索引全文索引删除索引MySQL事务概念事务的ACID特点事务之间的相互影响MySQ... 查看详情

mysql的事务特性,四种隔离级别及原理(代码片段)

一、事务的基本要素(ACID)1、原子性(Atomicity)事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执行过程中出错,会回滚到事务开始前的状态,所有的操作... 查看详情

mysql事务隔离级别的实现原理

文章目录一、什么是事务的隔离级别二、再看可重复读原理锁定读(当前读)一致性非锁定读(快照读)隐式锁定(两阶段锁)显式锁定三、总结一、什么是事务的隔离级别在数据库系统中,一个事务是指:由一系列数据库操作... 查看详情

crud工程师——数据库事物acid特性及隔离级别

事务的概念所谓事务是用户自定义的一个数据库操作序列,这些操作要么全做,要么全不做,是一个不可分割的工作单位。例如,在关系数据库中,一个事务可以是一条sql语句,一组sql语句或整个程序。事务和程序是两个概念,... 查看详情

oracle学习笔记事务acid及隔离级别

oracle学习笔记事务ACID及隔离级别隔离级别:isolationlevel一)事务的含义我们继续看事务,看事务的含义事务的含义有一个说法叫ACIDA就是原子性C就是一致性I是隔离性D是持久性叫ACID下面内容摘自老师的讲义事务的含义... 查看详情

mysql事务(代码片段)

文章目录二、MySQL事务2.1、事务的概念2.2、事务的ACID特点①原子性(Atomicity)②一致性(Consistency)③隔离性(Isolation)ⅰ查询全局事务隔离级别:ⅱ查询会话事务隔离级别ⅲ设置全局事务隔离级别ⅳ... 查看详情

mysql事务(代码片段)

文章目录二、MySQL事务2.1、事务的概念2.2、事务的ACID特点①原子性(Atomicity)②一致性(Consistency)③隔离性(Isolation)ⅰ查询全局事务隔离级别:ⅱ查询会话事务隔离级别ⅲ设置全局事务隔离级别ⅳ... 查看详情