前言:
我:数哥,数据库的 事务 知道不?
数哥:这你都不知道?
我:。。。。。。!
数哥:不就是,RU,RC,RR,Serializable
我:都解决了什么问题啊?
数哥:那我哪知道?
我:不知道了吧,解决了:脏读、不可重复读、幻读
mysql-事务隔离级别超级详细 解读
1、事务
1.1百科定义:
数据库事务( transaction)是访问并可能操作各种数据项的一个数据库操作序列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位
1.2事务性质(ACID)
-
原子性(Atomicity):事务中的全部操作在数据库中是不可分割的,要么全部完成,要么全部不执行。
-
一致性(Consistency):几个并行执行的事务,其执行结果必须与按某一顺序 串行执行的结果相一致。
-
隔离性(Isolation):事务的执行不受其他事务的干扰,事务执行的中间结果对其他事务必须是透明的。
-
持久性(Durability):对于任意已提交事务,系统必须保证该事务对数据库的改变不被丢失,即使数据库出现故障
**note:**事务的ACID特性是由关系数据库系统(DBMS)来实现的,DBMS采用日志来保证事务的原子性、一致性和持久性;事务的隔离性,DBMS是采用锁机制来实现的
1.3事务类型
1.3.1显式事务
显式事务又称自定义事务,是指用显式的方式定义其开始和结束的事务,当使用start transaction和 commit语句时则表示发生显式事务。
1.3.2隐式事务
隐式事务是指每一条数据操作语句都自动地成为一个事务,事务的开始是隐式的,事务的结束有明确的标记。即当用户进行数据操作时,系统自动开启一个事务,事务的结束则需手动调用 commit或 rollback语句来结束当前事务,在当前事务结束后又自动开启一个新事务。
1.3.3自动事务
自动事务是指能够自动开启事务并且能够自动结束事务。在事务执行过程中,如果没有出现异常,事务则自动提交;当执行过程产生错误时,则事务自动回滚。
1.4 事务控制
-- 开启事务
start transaction ;
-- 业务逻辑操作
-- 回滚事务
rollback;
-- 提交事务
commit ;
2、脏读、不可重复读、幻读
2.1 脏读
脏读发生在一个事务A读取了被另一个事务B修改,但是还未提交的数据。假如B回退,则事务A读取的是无效的数据,这个数据相对A来说 就是脏数据;
2.2 不可重复读
同一个事务中,根据同一个条件进行多次查询,结果不一样,就是不可重复读(主要针对 更新)
2.3 幻读
幻读是指同一个事务内多次查询返回的结果集(结果数量)不一样(比如增加了或者减少了行记录),主要针对 新增和删除
3、隔离级别
3.1 Serializable(串行化):
3.1.1 作用:
可避免脏读、不可重复读、幻读情况的发生。
3.1.2 开启命令
set session transaction isolation level Serializable;
3.2 Repeatable read(可重复读):
3.2.1 作用
可避免脏读、不可重复读情况的发生。
3.2.2 开启命令
set session transaction isolation level repeatable read;
3.3 Read committed(读已提交):
3.3.1 作用
可避免脏读情况发生。
3.3.2 开启命令
set session transaction isolation level read committed;
3.4 Read uncommitted(读未提交):
3.4.1 作用
最低级别,以上情况均无法保证。
3.4.2 开启命令
set session transaction isolation level read uncommitted;
查看隔离级别指令
-- mysql8.0 以后
select @@transaction_isolation;
-- mysql版本 8.0 之前
select @@tx_isolation;
4、源码
注意一下测试方便,模拟多线程,开启两个session(分别开启事务A 和 B),方便操作,两个session 都开启相同的隔离级别
(A 负责 查询)(B负责 写)
4.1 read uncommitted
-
4.1.1 启动()
set session transaction isolation level read uncommitted;
-
4.1.2 起始状态
select *
from user
where id = 1;
+----+------+----------+------+---------+
| id | name | password | salt | account |
+----+------+----------+------+---------+
| 1 | 11 | 1 | 1 | 1.0000 |
- 在B进行更新(不提交事务)
-- 开启事务
start transaction ;
-- 更新数据
update user
set name = '1'
where id = 1;
- A中 查询结果
select *
from user
where id = 1;
+----+------+----------+------+---------+
| id | name | password | salt | account |
+----+------+----------+------+---------+
| 1 | 1 | 1 | 1 | 1.0000 |
很明显,A结果已经变成了 1,读到了B 事务未提交的 修改结果;如果B 回滚 这就是无效数据;
这就是脏读;
4.2 Read committed
-
4.2.1 启动
set session transaction isolation level read committed;
-
4.2.2 起始状态
+----+------+----------+------+---------+
| id | name | password | salt | account |
+----+------+----------+------+---------+
| 1 | 11 | 1 | 1 | 1.0000 |
- 在B 进行更新(不提交事务)
-- 开启事务
start transaction ;
-- 更新数据
update user
set name = '1'
where id = 1;
commit;
- A中 查询结果
select *
from user
where id = 1;
+----+------+----------+------+---------+
| id | name | password | salt | account |
+----+------+----------+------+---------+
| 1 | 11 | 1 | 1 | 1.0000 |
很明显,S结果已经变成了 1,读到了B 事务未提交的 修改结果;
这就解决了脏读 的问题;
B 事务提交后 ,A事务再次查询
+----+------+----------+------+---------+
| id | name | password | salt | account |
+----+------+----------+------+---------+
| 1 | 1 | 1 | 1 | 1.0000 |
A事务 的两次操作 查询结果不一致,这就是一次事务 不可重复读的问题
4.3 Repeatable read
-
4.3.1 启动
set session transaction isolation level Repeatable read;
-
4.3.2 起始状态
+----+------+----------+------+---------+
| id | name | password | salt | account |
+----+------+----------+------+---------+
| 1 | 11 | 1 | 1 | 1.0000 |
- A中 查询结果(A 事务不结束)
-- 开启事务
start transaction ;
select *
from user
where salt = 1;
+----+------+----------+------+---------+
| id | name | password | salt | account |
+----+------+----------+------+---------+
| 1 | 11 | 1 | 1 | 1.0000 |
- 在B 进行更新(提交事务)
-- 开启事务
start transaction ;
-- 更新数据
update user
set name = '1'
where salt = 1;
-- 提交事务
commit;
A事务 再次查询
select *
from user
where id = 1;
+----+------+----------+------+---------+
| id | name | password | salt | account |
+----+------+----------+------+---------+
| 1 | 11 | 1 | 1 | 1.0000 |
通过结果可以看到:A 事务中(一个事务中 两次查询的 结果一致了-解决了不可重复读的问题)
事务B先执行以下:
mysql> start transaction ;
Query OK, 0 rows affected (0.00 sec)
mysql> select *
-> from user
-> where salt <= 3;
+----+------+----------+------+---------+
| id | name | password | salt | account |
+----+------+----------+------+---------+
| 1 | 4 | 4 | 1 | 4.0000 |
| 2 | 2 | 2 | 2 | 2.0000 |
| 3 | 4 | 4 | 1 | 4.0000 |
| 4 | 4 | 4 | 1 | 4.0000 |
| 5 | 4 | 4 | 1 | 4.0000 |
| 6 | 4 | 4 | 1 | 4.0000 |
+----+------+----------+------+---------+
6 rows in set (0.00 sec)
事务A重新开启事务,并执行
mysql> start transaction ;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into user(id, name, password, salt, account) VALUES (7,4,4,1,4);
Query OK, 1 row affected (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.01 sec)
事务B不提交再次查询
mysql> select * from user where salt <= 3;
+----+------+----------+------+---------+
| id | name | password | salt | account |
+----+------+----------+------+---------+
| 1 | 4 | 4 | 1 | 4.0000 |
| 2 | 2 | 2 | 2 | 2.0000 |
| 3 | 4 | 4 | 1 | 4.0000 |
| 4 | 4 | 4 | 1 | 4.0000 |
| 5 | 4 | 4 | 1 | 4.0000 |
| 6 | 4 | 4 | 1 | 4.0000 |
+----+------+----------+------+---------+
6 rows in set (0.00 sec)
事务B 提交后 再次查询
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from user where salt <= 3;
+----+------+----------+------+---------+
| id | name | password | salt | account |
+----+------+----------+------+---------+
| 1 | 4 | 4 | 1 | 4.0000 |
| 2 | 2 | 2 | 2 | 2.0000 |
| 3 | 4 | 4 | 1 | 4.0000 |
| 4 | 4 | 4 | 1 | 4.0000 |
| 5 | 4 | 4 | 1 | 4.0000 |
| 6 | 4 | 4 | 1 | 4.0000 |
| 7 | 4 | 4 | 1 | 4.0000 |
+----+------+----------+------+---------+
7 rows in set (0.00 sec)
我们会发现,what? 说好的 **“幻读”**呢;
其实这里 单纯的隔离级别RR ;不能解决 的问题,之所以 没有这个问题 ,是因为 mysql的 MVCC 机制
4.4 Serializable
这种隔离级别 不做 过多赘述,没有 幻读问题,但是 大大较低了 性能;
个人水平有限,如有问题,欢迎大家留言指出,虚心接受,及时更正
如果大家觉得,还可以,烦请点赞收藏,谢谢