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语句(insertupdatedelete)执行,标志事务的开启

"命令: 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的事务事务的隔离级别事务的保存点的主要内容,如果未能解决你的问题,请参考以下文章

MySQL 中事务以及事务隔离级别的详解

不错MySQL 事务隔离级别

MySQL的默认事务隔离级别是?

[JavaWeb-MySQL]事务的四大特征和隔离级别

啥是 MySQL 事务隔离级别?

如何更改mysql事务隔离级别