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