MySQL的事务事务的隔离级别事务的保存点
Posted KJ.JK
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL的事务事务的隔离级别事务的保存点相关的知识,希望对你有一定的参考价值。
文章目录
💨往期精彩知识👇
💖Spring中的创建对象的三种方式、第三方资源配置管理详细描述及使用(XML版完结篇)
💖Spring中的bean的配置、作用范围、生命周期详细描述及使用(XML版上篇)
💖Spring中的依赖注入、setter与构造器注入、自动装配与集合注入详细描述及使用(XML版中篇)
🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈
🍂个人博客首页: KJ.JK
💖系列专栏:JavaEE进阶教程系列
一、什么是事务?
- 在mysql中的事务(Transaction)是由存储引擎实现的,在MySQL中,只有InnoDB存储引擎才支持事务
- 事务处理可以用来维护数据库的完整性,保证成批的 SQL 语句要么全部执行,要么全部不执行
- 事务用来管理 DDL、DML、DCL 操作,比如 insert,update,delete 语句,默认是自动提交的
二、事务的开启关闭提交操作
1、开启事务: Start Transaction 任何一条DML语句(insert、update、delete)执行,标志事务的开启 "命令: BEGIN 或 START TRANSACTION" -------------------------------------------------------------------------------------------------------- 2、提交事务: Commit Transaction 成功的结束,将所有的DML语句操作历史记录和底层硬盘数据来一次同步 "命令:COMMIT" -------------------------------------------------------------------------------------------------------- 3、回滚事务: Rollback Transaction 失败的结束,将所有的DML语句操作历史记录全部清空 "命令:ROLLBACK" -------------------------------------------------------------------------------------------------------- 之前的所有SQL操作其实也有事务,只是MySQL自动帮我们完成的,每执行一条SQL时MySQL就帮我们自动提交事务,因此如果想要手动控制事务,则必须关闭MySQL的事务自动提交 -- 查看事务默认提交状态,0为手动提交,1为自动提交 select @@autocommit; 在MySQL中直接用 SET 来改变 MySQL 的自动提交模式: "set autocommit=0 禁止自动提交" "set autocommit=1 开启自动提交"
代码演示:
start transaction; -- 也可以用begin; -- begin; update account set money=money-200 where id =1; update account set money=money+200 where id =2; -- 提交事务 commit; -- 回滚事务 rollback; select * from account;
三、事务的保存点
"定义:" 在回滚事务时,事务内的所有操作都将撤销,如果希望撤销一部分,可以用保存点来实现
"语法:" savepoint 保存点名;
在设置保存点后,使用以下语句将事务回滚到指定保存点
"语法:" rollback to savepoint 保存点名;
"例子:" savepoint s2;
rollback to savepoint s2;
"删除保存点语法:" release savepoint保存点名;
四、事务的特性
- 原子性(Atomicity):事务是一个不可分割的整体,事务开始后的所有操作,要么全部完成,要么全部不做
- 一致性(Consistency):系统从一个正确的状态,迁移到另一个正确的状态
- 隔离性(Isolation):每个事务的对象对其他事务的操作对象互相分离,事务提交前对其他事务不可见
- 持久性(Durability):事务一旦提交,则其结果是永久性的
五、事务的隔离级别
读未提交(Read uncommitted)
一个事务可以读取另一个未提交事务的数据,最低级别,任何情况都无法保证,会造成脏读读已提交(Read committed)
一个事务要等另一个事务提交后才能读取数据,可避免脏读的发生,会造成不可重复读,这是大多数数据库默认的隔离级别可重复读(Repeatable read)
就是在开始读取数据(事务开启)时,不再允许修改操作,可避免脏读、不可重复读的发生,但是会造成幻读 这是MySQL 的默认隔离级别,它确保了 “一个事务中多个实例在并发读取数据的时候会读取到一样的数据”;
- 串行(Serializable)
是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读,但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用
代码演示:
-- 查看隔离级别 show variables like '%isolation%'; -- 查看全局隔离级别 select@@global.transaction_isolation; --全局的隔离级别: 影响所有连接MySQL用户 -- 查看当前会话的隔离级别 select@@session.transaction_isolation; --当前会话隔离级别: 只影响当前正在登录MySQL服务器的用户 -- 查看下一个事务的隔离级别 select@@transaction_isolation; --下一个事务的隔离级别:仅对当前用户的下一个事务操作有影响 -- 设置隔离级别 -- 设置read uncommitted(读未提交) -- 这种隔离级别会引起脏读,A 事务读取到B事务没有提交的数据 set session transaction isolation level read uncommitted; -- 设置read committed(不可重复读) -- 这种隔离级别会引起不可重复读,A事务在没有提交事务之前,可看到数据不一致 set session transaction isolation level read uncommitted; -- 设置 repeatable read(可重复读)(MySql默认) -- 这种隔离级别会引起幻读,A事务在提交之前和提交之后看到的数据不一致 -- set session transaction isolation level repeatable read; -- 设置 serializable(可串行化) -- 这种隔离级别比较安全,但是效率低,A事务操作表的时候,表会被锁起来,B事务不能操作 set session transaction isolation level read serializable;
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交(Read uncommitted) | 是 | 是 | 是 |
读已提交(Read committed) | 否 | 是 | 是 |
可重复读(Repeatable read) | 否 | 否 | 是 |
串行化(Serializable) | 否 | 否 | 否 |
- 不可重复读例子:
张三的银行卡里面有250块钱,第一次查看为250块钱,紧接着张三的妻子把这个250块钱转出去了,张三第二次查询的时候,
发现银行卡里面为0块钱,“第一次与第二次读取的记录不同,这就是不可重复读”(“两次查看的记录不一样”)
幻读例子:
指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发生幻读
幻读主要针对 “插入的场景”,就是 “同一个事务内,多次读取,读出的记录条数发生了变化”
作者:KJ.JK
文章对你有所帮助的话,欢迎给个赞或者 star,你的支持是对作者最大的鼓励,不足之处可以在评论区多多指正,交流学习
啥是 MySQL 事务隔离级别?
之前发过一篇文章,简单了解 MySQL 中相关的锁,里面提到了,如果我们使用的 MySQL 存储引擎为 InnoDB
,并且其事务隔离级别是 RR
可重复读的话,是可以避免幻读的。
但是没想到,都 1202
年了都还有人杠,说 InnoDB 的 RR 隔离级别下会出现幻读,只能依靠 gap 和 next-key 这两个锁来防止幻读
,最开始我还以为是他真的不知道这个点,就跟他聊,最后聊下来发现,发现是在钻牛角尖。
这个在下面讲到 可重复读 的隔离级别时会讲。
本来我觉得事务隔离级别这玩意儿太简单没啥可讲的,但是经过了上面这件事,我打算详细的把事务隔离给讲讲。接下来顺便就把 InnoDB
所有的事务隔离级别给搂一遍。
ACID
在聊事务隔离级别之前,我们需要知道 ACID 模型。
分别代表:
- Atomicity 原子性
- Consistency 一致性
- Isolation 隔离型
- Durability 持久性
原子性,代表 InnoDB 事务中,所有的操作要么全部成功,要么全部失败,不会处于某个中间状态。说的更通俗一点,如果事务 A 失败,其所做的所有的更改应该全部回滚。
一致性,主要是保护数据的一致性,防止由于数据库的崩溃而导致的数据一致性问题。举个例子,我们更新 MySQL 的数据,更新的数据会先到 InnoDB 的 Buffer Pool 中,如果此时 MySQL 所在的机器突然意外重启了,如果 InnoDB 没有崩溃恢复机制,之前更新的数据就会丢失,数据的一致性问题就出现了。
隔离性,主要是指事务之间的隔离,再具体一点,就是我们本篇文章要讨论的事务隔离级别了。
持久性,主要是指我们新增或者删除了某些数据,一旦成功,这些操作应该需要被持久化到磁盘上去。
ACID 模型可以理解成数据库的设计范式,主要关注点在数据数据、及其本身的可靠性。而 MySQL 中的 InnoDB 就完全遵守 ACID 模型,并且在存储引擎层就支持数据一致性的校验和崩溃恢复的机制。
而 ACID 中的隔离型,就是我们这篇文章中讨论的重点。
事务隔离级别
有很多文章上来就直接介绍事务隔离级别的种类,这个种类啥意思,那个种类怎么用。但我认为应该先了解为什么需要事务隔离级别,以及事务隔离级别到底解决了什么问题,这才是关键。
我们知道 InnoDB 中同时会有多个事务对数据进行操作,举一些例子:
- 假如事务A需要查询
id=1
的数据,但是事务A查询完毕之后,事务B对id=1
的数据做了更新,那此时事务A再次执行查询,应该看到更新前的数据还是更新后的数据? - 或者还是上面那个例子,事务A读取了事务B的数据,但是如果事务B进行回滚了怎么办?事务A的数据不就变成了脏数据?
- 又或者事务A读取了
1-3点
的日程安排,有4条,但是事务A读取完成后事务B又向1-3
点这个时间段插入了一条新的安排,那么事务A如果再次读取,应该显示4条日程安排还是5条?
以上的这些问题,就需要事务隔离级别来回答了。其实以上的三种情况分别对应不可重复读、脏读和幻读。InnoDB 通过事务隔离级别分别的解决了上面的问题。所有的事务隔离级别如下:
- READ UNCOMMITTED 读未提交
- READ COMMITTED 读已提交
- REPEATABLE READ 可重复读
- SERIALIZABLE 串行化
InnoDB 默认的事务隔离级别为 REPEATABLE READ
。
读未提交
如果事务B此时出错了进行了回滚,那么事务A读取到的数据就成为了脏数据,从而造成脏读。
如果事务B又更新事务A读取的数据,那么事务A再次读取,读取到了事务B修改的结果,这造成了不可重复读。
而如果事务B又新增了数据,事务A再次读取,会读取到事务B新增的数据,这造成了幻读。
所以总结来说,在读未提交这个隔离级别下,会造成以下的问题:
- 脏读
- 不可重复读
- 幻读
读已提交
如果事务B更新了事务A读取到的数据,并且提交,那么当事务A再次进行读取,就会读取到其他事务的变更,就造成了不可重复读。
同理,如果事务B新增了数据并且提交,事务A再次进行读取时拿到了事务B刚刚提交的数据,这就造成了幻读。
所以总结来说,在读已提交的隔离级别下,会造成:
- 不可重复读
- 幻读
可重复读
在可重复读场景下,不会出现脏读、不会出现不可重复读,可能会出现幻读。
无论事务B做了什么操作,事务A查询到的 id=1
的数据都是张三。
但是,在某些情况下,还是可能会出现 幻读。可重复读 只是在某些情况下会产生幻读,但绝对不是 InnoDB 无法避免幻读
。首先,InnoDB 在 RR 隔离级别下有很明确的解决幻读的方式,那就是——临键锁,一种组合了 gap 锁和记录锁的锁。
接下来举个例子来看在 RR 隔离级别下,什么情况会出现幻读,什么情况下不会出现幻读。首先是 可能会出现幻读。
SELECT * FROM `student` WHERE `id` > 1;
由于 InnoDB 有 MVCC 来进行多事务的并发,此时 SELECT
走的是快照读,不会加锁,所以无论插入多少 id > 1
的数据,在同一个事物内执行上述的 SQL 是不会出现幻读的。
但是如果显示的进行加锁,就会出现幻读。
SELECT * FROM `student` WHERE `id` > 1 FOR UPDATE
为啥这样就会有问题呢?对 SELECT
显示的进行加锁之后,无论是加的共享锁还是排他锁,都会进行 当前读,而一旦执行了当前读,就能够读取到其他事物提交的 id > 1
的数据。
串行化
这样从根本上就避免了并发的问题,但是这样会使得 MySQL 的性能下降。因为现在同一时间只能有一个事务在运行。
EOF
关于事务隔离级别就先介绍到这,之后有时间了就把 事务隔离级别 的底层原理给搂一遍。
以上是关于MySQL的事务事务的隔离级别事务的保存点的主要内容,如果未能解决你的问题,请参考以下文章