目录
什么是事务?
事务就是一组DML语句组成,这些语句在逻辑上存在相关性,这一组DML要么全部成功,要么全部失败,是一个整体。MySQL提供一种机制,保证我们达到这样的效果。事务还规定不同的客户端看到的数据是不相同的。
事务就是要做的或所做的事,主要用于处理操作量大,复杂度高的数据。假设一种场景,从学校毕业后,学校教务系统后台的MySQL中,不再需要你的数据,要删除你的所有信息,那么要删除你的基本信息的同时也删除和你有关的其他信息:各科成绩,在校表现等等。这样就需要多条MySQL语句构成,那么所有这些操作合起来,就构成了一个事务。
一个MySQL数据库不止一个事务在运行,同一时刻,甚至有大量的请求被包装成事务,在向MySQL服务器发起事务处理请求。而每条事务至少一条SQL,如果大家都访问同样的表数据在不加保护的情况,就绝对会出现问题。甚至因为事务由多条SQL构成,那么也会存在执行到一半出错或者不想再执行的情况,那已经执行的该怎么办呢?
所以一个完整的事务,绝对不是简单的SQL集合,还需要满足如下四个属性:
- 原子性:一个事务中所有的操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样
- 一致性:在事务开始之前和事务结束之后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作
- 隔离性:数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(serializable)
- 持久性:事务处理结束后,对数据的修改就是永久的,即使系统故障也不会丢失
上面四个属性可以简称为ACID
原子性(Atomicity,或称不可分割性)
一致性(Consistency)
隔离性(Isolation,又称独立性)
持久性(Durability)
为什么要有事务?
事务是被MySQL编写者设计出来,本质是为了当应用程序访问数据库的时候,事务能够简化我们的编程模型,让我们不需要去考虑各种潜在错误和并发问题,当我们使用事务时,要么提交要么回滚,我们不会考虑网络异常、服务器宕机,同时更改一个数据怎么办。
所以事务本质上是为了应用层服务的,不是伴随着数据库系统天生就有的
(后面将一行信息称为一行记录)
对事务的支持
在MySQL中只有使用了InnoDB数据库引擎的数据或表才支持事务,MyISAM不支持
查看数据库引擎
sql
show engines; --表格显示
show engines \G --行显示


可以看到InnoDB是支持transactions(事务)的,MyISAM是不支持的
事务的提交方式
事务的常见提交方式有两种:
自动提交
手动提交
show variables like 'autocommit';
命令来查看事务的提交方式

可以看到是自动提交的
用set来改变MySQL的自动提交方式
set autocommit=0; 禁止自动提交
set autocommit=1; 开启自动提交

事务的常见操作方式
为了便于演示,我们将MySQL的默认隔离级别设置成读未提交。
set global transaction isolation level READ UNCOMMITTED;
进行查看
8.0以下版本是select @@tx_isolation;
以上变为了select @@transaction_isolation;

创建测试表

查看多少人在连接MySQL
show processlist;

(1) 启动事务并设置一个保存点插入一条数据,另一台机器也启动事务

执行 rollback to save3; 再执行rollback to save1 命令来进行回滚
下面分别为 未回滚、回滚到save3,回滚到save1

**(2)**不做savepoint 设置保存点
插入数据后直接rollback

就直接将启动事务后的所有操作

(3) begin 开启事务,插入数据后,commit递交,再rollback看看

commit提交后就回滚不了了回滚操作是在事务运行期间可以进行回滚,事务提交后无法回滚

事务异常验证
(1) begin开启事务
插入一条数据,然后ctrl+\ 使MySQL崩溃
分别查看表里的数据

可以看到没有commit提交

如果commit提交了就不会因为客户端崩溃异常来回滚,将数据被提交到数据库里
(2) begin开启事务
插入一条数据,然后直接关闭终端
依旧和上面的结果一样
(3) begin与autocommit自动提交
begin手动开启事务,手动提交的方式与我们show variables like 'autocommit' 查看到的提交方式为自动时两码事
set autocommit=0; 将自动提交关闭结果依然不会改变
自动提交是否开启对上面的begin手动开启的事务结果是没有影响的
begin操作会自动更改提交方式,不受MySQL是否自动提交影响
(4) 关闭自动提交
set autocommit=0; 关闭自动提交后,执行一条删除列的sql语句再ctrl+\ 发送退出信号直接退出客户端
由于没有提交,导致自动进行了回滚,必须commit手动提交,这条sql才会永久有效

而set autocommit 将自动提交开启再ctrl+\ 数据就不会回滚回去

结论
输入begin或者start transaction,事务便必须要通过commit提交,才会持久化,与是否设置set autocommit 无关
事务可以手动回滚,当操作异常时,MySQL会自动回滚
对于InnoDB 每一条sql语言都默认封装成事务,自动提交。(select有特殊情况,因为MySQL有MVCC)
上方的例子体现了事务本身的原子性(回滚)、持久性(commit)
事务的注意事项
- 如果没有设置保存点,也可以回滚,只能回滚到事务的开始。
- 直接使用 rollback(前提是事务 还没有提交) 如果一个事务被提交了(commit),则不可以回退(rollback)
- 可以选择回退到哪个保存点
- InnoDB 支持事务, MyISAM 不支持事务
- 开始事务可以使 start transaction 或者 begin
事务隔离级别
如何理解隔离性
- MySQL服务可能会同时被多个客户端进程(线程)访问,访问的方式以事务方式进行
- 一个事务可能由多条SQL构成,也就意味着,任何一个事务,都有执行前,执行中,执行后的阶段。而所谓的原子性,其实就是让用户层,要么看到执行前,要么看到执行后。执行中出现问题, 可以随时回滚。所以单个事务,对用户表现出来的特性,就是原子性。
- 但是毕竟所有事务都要有个执行过程,那么在多个事务各自执行多个SQL的时候,就还是有可能会出现互相影响的情况。比如:多个事务同时访问同一张表,甚至同一行数据。
- 就如同你妈妈给你说:你要么考到第x名,要不就不用学。至于你怎么学,中间有什么困难,你妈妈不关心。那么你的学习,对你妈妈来讲,就是原子的。那么你学习过程中,很容易受别人干扰,此 时,就需要将你的学习隔离开,保证你的学习环境是健康的。
- 数据库中,为了保证事务执行过程中尽量不受干扰,就有了一个重要特征:隔离性
- 数据库中,允许事务受不同程度的干扰,就有了一种重要特征:隔离级别
-
在事务场景中,隔离是必要的,运行中的事务进行相互隔离
-
在事务运行中"不会"出现互相干扰-->才有的隔离性
-
根据影响程度的不同-->隔离级别
隔离级别
- 读未提交【Read Uncommitted】:在这种级别,所有的事务都可以看到其他事务没有提交的执行结果,相当于没有任何隔离性(实际中不可能使用),会出现很多并发问题如脏读,幻读,不可重复读等,我们上面为了做实验方便,用的就是这个隔离性。
- 读提交【Read Committed】:该隔离级别是大多数数据库的默认的隔离级别(不是 MySQL 默认的)。它满足了隔离的简单定义:一个事务只能看到其他的已经提交的事务所做的改变。这种隔离级别会引起不可重复读,即一个事务执行时,如果多次 select, 可能得到不同的结果。
- 可重复读【Repeatable Read】: 这是 MySQL 默认的隔离级别,它确保同一个事务,在执行 中,多次读取操作数据时,会看到同样的数据行。但是会有幻读问题。
- 串行化【Serializable】: 这是事务的最高隔离级别,它通过强制事务排序,使之不可能相互冲突, 从而解决了幻读的问题。它在每个读的数据行上面加上共享锁,。但是可能会导致超时和锁竞争 (这种隔离级别太极端,实际生产基本不使用)
隔离界别如何实现:隔离,基本都是通过锁来实现的,不同的隔离级别,锁的使用是不同的。常见的有,表锁,行锁,读锁,写锁,间隙锁(GAP),Next-Key锁(GAP+行锁)等等。
查看隔离性
MySQL8.0 之前
sqlSELECT @@global.tx_isolation; --查看全局隔级别 SELECT @@session.tx_isolation; --查看会话(当前)全局隔级别 SELECT @@tx_isolation; --默认同上MySQL8.0之后
sql-- 查看全局事务隔离级别 SELECT @@global.transaction_isolation; -- 查看当前会话的事务隔离级别 SELECT @@session.transaction_isolation; -- 默认查看当前会话的事务隔离级别 SELECT @@transaction_isolation;
设置隔离级别
sql
-- 设置当前会话 or 全局隔离级别语法
SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ
COMMITTED | REPEATABLE READ | SERIALIZABLE}
(1) 设置session局部读提交 更改会话隔离级别

另一个会话中是没有改变隔离性的

(2)设置global全局 为serializable
、
另一个会话中的全局隔离级别也受影响,但是会话隔离级别没变

重新进入MySQL,我们发现会话隔离级别和全局隔离级别一致了
两个会话隔离级别查询结果都如下图所示

读未提交
我们set global transaction isolation level read uncommitted;设置为读未提交后重新进入MySQL
执行一条插入命令,执行一条修改命令,都能在没提交的时候再其他终端看到(一个事务在执行中,读到另一个执行中事务的更新 ,或其他操作,但是未commit提交的数据 -->这种现象叫做脏读),rollback回滚后,查到修改前的数据


读提交
set global transaction isolation level read committed; 后重新进入MySQL
begin开始事务后插入一个数据修改一个数据,另一个会话是查不到修改后的表的,自己的会话查到的是已经修改的表,commit提交后,其他会话才能查到,即使另一个会话的begin没有结束没有commit

另一个会话在上面会话commit提交前后查询如下所示

同一个事务内,不同时间段同样的读取(依旧还在事务操作中),却读到了不同的值这种的现象叫做不可重复读
不可重复读是一个问题吗??
如果修改了数据,区间查找例如查找一个公司员工工资分别为1000~2000,2001~3000,3001~4000的,如果查找时,将一个员工工资进行更改,可能会多次找到
可重复读
MySQL默认隔离级别 Repeatable Read
systemctl restart mysql
重置系统变量
会话隔离级别和全局隔离级别也变为MySQL默认的

两台终端A与B
都begin开启事务,终端A做的命令,终端B是查询不到的,即使终端A提交了,必须终端B也commit提交

查询结果如下

必须终端A提交了,B再去提交后才可以查询到

多次查看,发现终端A在对应事务中insert的数据,在终端B的事 务周期中,也没有什么影响,也符合可重复的特点。但是,一般的数据库在可重复读情况的时候,无法屏蔽其他事务insert的数据(为什么?因为隔离性实现是对数据加锁完成的,而insert待插入的数据因为并不存在,那么一般加锁无法屏蔽这类问题),会造成虽然大部分内容是可重复读的,但是insert的数据在可重复读情况被读取出来,导致多次查找时,会多查找出来新的记录 ,就如同产生了幻觉。这种现象,叫做幻读 (phantom read)。很明显,MySQL在RR级别 的时候,是解决了幻读问题的(解决的方式是用Next-Key锁 (GAP+行锁)解决的。
串行化
serializable
对所有操作全部加锁,进行串行化,不会有问题,但是只要串行化,效率很低,几乎完全不会被采用
set global transaction isolation level serializable;
全局隔离级别更改为串行化
两个终端begin开启事务,A事务执行命令会卡住


必须B终端提交,A终端才能顺利执行


总结
- 隔离级别越严格,安全性越高,数据库并发性能就越低,往往需要在两者之间找一个平衡点。
- 不可重复读的重点是修改和删除:同样的条件,你读取过的数据,再次读取出来发现值不一样了幻读的重点在于新增:同样的条件,第一次和第二次读出来的记录数不一样
- MySQL的默认隔离级别是可重复读,一般情况下不要修改
- 上面的例子可以看出,事务也有长短事务这样的概念。事务间互相影响,指的是事务在并行执行的时候,即都没有commit的时候,影响会比较大
对一致性的理解
- 事务执行的结果,必须使数据库从一个一致性状态,变到另一个一致性状态,当数据库只包含事务成功提交的结果时,数据库处于一致性状态。如果系统运行发生中断,某个事务尚未完成而被迫中断,而改未完成的事务对数据库所做的修改已被写入数据库,此时数据库就处于一种不正确(不一致)的状态。因此一致性是通过原子性来保证的。
- 其实一致性和用户的业务逻辑强相关,一般MySQL提供技术支持,但是一致性还是要用户业务逻辑做支撑,也就是一致性是由用户决定的。
这篇就到这里(づ ̄3 ̄)づ╭❤~

