MySQL数据库的事务和MySQL隔离级别分析
Posted 飞人01_01
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL数据库的事务和MySQL隔离级别分析相关的知识,希望对你有一定的参考价值。
hello 大家好!今天来聊聊关于数据库事务的一些事情。说到事务,我相信大家应该都不陌生,多多少少也知道其中的一些事情。今天着重聊聊mysql中事务的隔离级别。上车上车……
文章目录
一、什么是事务?
事务指的是逻辑上的一组操作集合,组成这组集合的各个单元,要么全部成功,要么全部失败。在不同的环境中,都可以有事务,那么在数据库中,就称为数据库事务。
举个实际例子:
假设我现在要给同学微信转100块钱,这件事情大致就可以分为两个步骤:
- 先从我的微信钱包中减100块钱;
- 同学的微信钱包中加100块钱。
分为这两个步骤,那么就有可能出现一种极端情况,那就是第一个步骤完成了,但此时微信的后台服务器挂了,可能导致同学的微信钱包中并没有增加100块钱。
为了处理这样的极端情况,就将这两个步骤“合并”到一起,合并成一个事务。只有在这两个步骤都完成之后,才能说这个事务完成了;如果出现了上述的极端情况,则表示事务失败,此时数据库就会回滚数据,也就是将第一个步骤中减去100块钱再自动加回来,将全部数据恢复到事务开始之前的状态。
说清楚了事务之后,就还需要知道事务的四大特性(ACID):
- 原子性(Atomicity):事务是数据库的逻辑工作单位,事务中包含的各种操作,要么全都做,要么全都不做。一个事务就是一个原子。
- 一致性(Consistency):事务执行的结果必须是让数据库从一个一致性状态变为另一个一致性状态。当数据库中只包含成功事务的提交结果时,就说数据库处于一致性状态。相反,数据库中有失败事务,并且这个失败事务已经将一部分数据永久性地写入数据库,造成数据不一致,就称为不一致性状态。
- 隔离性(Isolation):一个事务的执行,不能对其他进程或事务造成干扰。也就是说一个事务内部的操作以及使用的数据对其他并发事务是隔离的,并发执行的各个事务之间不能互相干扰。
- 持续性(Durability):也称为永久性。指的是某个事务的执行过程中,对数据所做的所有改动都必须在事务成功结束之前保存到物理存储设备中。这样能够保证,所做的修改在任何系统瘫痪时不至于丢失。
在数据库中,最为关键的特性就是一致性,事务的一致性就是通过原子性、隔离性和持久性来保证的。
二、MySQL中的事务和隔离级别
如何实现mysql中的事务?有如下代码:
start transaction; -- 开启事务
-- 事务里面的所有逻辑操作
rollback; -- 回滚数据(事务失败时)
commit; -- 提交事务(事务成功时)
举个例子:
start transaction; -- 开启事务
delete from emp where empno = 1111;
delete from emp where empno = 1234;
delete from emp where empno = 2222;
commit; -- 提交事务
上述代码,就是将3条删除操作合并到一起,搞成一个事务执行。除了这种用法之外,还有这样的一种用法:
start transaction; -- 开启事务
delete from emp where empno = 1111;
delete from emp where empno = 1234;
savepoint sp1; -- 保存点,用于标志回滚数据的起始位置
delete from emp where empno = 2222;
rollback to sp1; -- 回滚到sp1标志位处
commit; -- 提交事务
上述代码提到了保存点的概念,其实你就可以理解为一个标记,第6行的代码进行回滚时,就会回滚到这个标记处。如果不添加保存点的话,默认使用回滚,就是回滚到整个事务的开始处。
隔离级别:就是将MySQL中的隔离,分为了四个等级,分别是
- 读取未提交内容(Read Uncommitted)
- 读取已提交内容(Read Committed)
- 可重读(Repeatable Read)
- 可串行化(Serializable)
造成的数据不一致的问题:
- 脏读
- 不可重复读
- 幻读
具体的演示效果如下:
以下全部测试用例,均在同一台MySQL服务器中执行,电脑中开启两个窗口,模拟两个不同的人并发操作数据库。这两个人分别叫A和B。首先测试之前,我们需要关闭MySQL中自动提交事务的机制,如下图所示:
select @@autocommit; -- 查看自动提交是否开启
set autocommit = 0; -- 关闭自动提交
set autocommit = 1; -- 开启自动提交
测试数据
create table student(id int primary key, name varchar(20));
insert into student values(1, '胡歌');
insert into student values(2, '张费');
insert into student values(3, '熊二');
如下图,A和B都要关闭自动提交事务
。
正式的演示开始:
1、 读取未提交内容(Read Uncommitted)
具体的SQL语句:
-- 首先设置数据库的隔离级别
A: set session transaction isolation level read uncommitted;
B: set session transaction isolation level read uncommitted;
A: start transaction; -- 开启A的事务
B: start transaction; -- 开启B的事务
A: select * from student; -- 查看A的数据
B: select * from student; -- 查看B的数据
A: insert into student values(4, '彭于晏'); -- A插入新的数据,但未提交事务
A: select * from student; -- 查看A的数据,会出现新插入的数据
B: select * from student; -- 查看B的数据,也出现A插入的数据
A: commit;
B: commit; -- A和B都提交事务,完成测试
脏读:就是在上述第8行代码能够看到,B能够查看到A新插入的数据(还未提交事务前),理应来说,A都还没提交事务,B是不可能看得到的。这就是脏读的现象。
就好比说,学校的小道消息传:某某同学谈恋爱了,但这只是小道消息,并不是板上钉钉的事,到时候一问人家,人家说没谈恋爱,这就很尴尬了。
2、 读取已提交内容(Read Committed)
上图SQL语句:
-- 首先设置数据库的隔离级别
A: set session transaction isolation level read committed;
B: set session transaction isolation level read committed;
A: start transaction; -- 开启A的事务
B: start transaction; -- 开启B的事务
A: select * from student; -- 查看A的数据
B: select * from student; -- 查看B的数据
A:update student set name = '石昊' where id = 3; -- A更新数据
A: select * from student; -- 查看A的数据,会出现更新的数据
B: select * from student; -- 查看B的数据,并不会出现A更新的数据。没有了脏读
A: commit; -- A提交事务后
B: select * from student; -- 查看B的数据,出现了A更新的数据,出现了“不可重复读”
B: commit; -- B提交事务,完成测试
上述SQL语句中,第11行代码处,查看B的数据,因为此时A并没有提交(commit),所以B是看不到更新的数据的,没有脏读了;A提交事务后(commit),此时再查看B的数据,就出现了A更新的数据,理应来说,在用一个事务中,查询到的所有数据都应该是相同的,不能因为其他人修改了数据,影响到另外一个人的查询结果。这也就是所谓的不可重复读。
3、 可重读(Repeatable Read)
A: set session transaction isolation level repeatable read;
B: set session transaction isolation level repeatable read;
A: start transaction; -- 开启A的事务
B: start transaction; -- 开启B的事务
A: select * from student; -- 查看A的数据
B: select * from student; -- 查看B的数据
A:update student set name = '石昊' where id = 3; -- A更新数据
A: select * from student; -- 查看A的数据,会出现更新的数据
B: select * from student; -- 查看B的数据,并不会出现A更新的数据。没有了脏读
A: commit; -- A提交事务后
B: select * from student; -- 查看B的数据,也没出现A更新的数据,没有了“不可重复读”
A: start transaction; -- A再次开启事务
A: insert into student values(4, '张三'); -- A插入新的数据
A: commit; -- A提交事务,产生持久化存储
A: select * from student; -- 查看A的数据,会新增一条记录(4,张三)
B: select * from student; -- 查看B的数据,没有查看到A新增的数据,出现了“幻读”
B: insert into student values(4, '张三'); -- B插入和A同样的数据,就会报错
B: commit; -- B提交事务,完成测试
上述SQL语句中,在A数据库进行更新操作,无论是否A提交事务,在B数据库这边是看不到A更新的数据,避免了“脏读”和“不可重复读”,但是在A数据库进行新增或删除操作,在B这边还是看不到新添加的、新删除的数据,这就产生了“幻读”。最大的影响就是:B并不知道此时数据库中已经插入了新的数据,如果此时B再次插入和A相同的数据,就会报错,因为插入的数据已经存在了。如下图:
4、 可串行化(Serializable)
A: start transaction; -- A开启事务
B: start transaction; -- B开启事务时,就会进行等待,因为A的事务还没结束
A: commit; -- A提交事务后
B: -- 此时B才会开启事务,执行B的操作
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交 | Y | Y | Y |
读已提交 | Y | Y | |
可重读 | Y | ||
可串行化 |
serializable完全锁定字段,若一个事务来查询同一份数据就必须等待,直到上一个事务完成并解除锁为止,是完整的隔离级别,会锁定对应的数据表格,所以会产生效率问题。
好啦,以上全部就是关于数据库的事务和隔离级别的介绍,今天就到此结束啦,我们下期见!!!
以上是关于MySQL数据库的事务和MySQL隔离级别分析的主要内容,如果未能解决你的问题,请参考以下文章