# learn-tx **Repository Path**: jslssz/learn-tx ## Basic Information - **Project Name**: learn-tx - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2020-05-17 - **Last Updated**: 2021-07-16 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README #### MySQL本地事物 实验环境:mysql8.0 事物的四大特性:ACID,原子性,一直性,隔离性,持久性 查看默认隔离级别:show variables like 'transaction_isolation'; 事物的隔离级别:读未已提交,读已提交,可重复读,串行化 #### 读未提交 所有事物可以读取到其他事物未提交的事物执行结果,读取未提交的数据称为脏读。 设置当前隔离级别为 读未提交 ```mysql SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; ``` 关闭会话,重新打开,查看隔离级别是否设置成功 ```mysql SHOW VARIABLES LIKE 'transaction_isolation'; ``` ```sql # A事物开启 BEGIN; # 第一次查询 SELECT * FROM USER; # 第二次查询 读到了B事物为提交的内容 SELECT * FROM USER; # 第三次查询 出现脏读现象 SELECT * FROM USER; # 结束A事物 COMMIT; ``` ```sql # B事物开启 BEGIN; # 更新id为3的名字,不提交,A事物继续读 UPDATE USER SET NAME="miya" WHERE id = 3; #回滚 ROLLBACK; ``` #### 读已提交 设置读已提交的隔离级别 ```mysql SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED; ``` 关闭会话,查询是否设置成功 ```mysql SHOW VARIABLES LIKE 'transaction_isolation'; ```` A事物 ```mysql # A事物开启 BEGIN; # 第一次查询 SELECT * FROM USER; # 第二次查询 读到了B事物已提交的内容 但是在同一个事物之内,两次读取的内容不一样,即产生了不可重复读的问题 SELECT * FROM USER; # 结束A事物 COMMIT; ``` B事物 ```mysql # A事物开启 BEGIN; # 第一次查询 SELECT * FROM USER; # 第二次查询 读到了B事物已提交的内容 但是在同一个事物之内,两次读取的内容不一样,即产生了不可重复读的问题 SELECT * FROM USER; # 结束A事物 COMMIT; ``` #### 可重复读 【特别注意:笔者在用小海豚SQLyog是失效的,必须开启两个独立的客户端才可以】 设置当前隔离级别为可重复读,这也是mysql的默认隔离级别 ```mysql SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ; ``` 关闭会话,重新打开,查看隔离级别是否设置成功 ```mysql SHOW VARIABLES LIKE 'transaction_isolation'; ``` ```mysql # A事物开启 BEGIN; # 第一次读 SELECT * FROM USER; # B事物更新数据,未提交,第二次读,还是原来的数据 SELECT * FROM USER; # B事物提交,第三次读,还是原来的数据 SELECT * FROM USER; # 提交 COMMIT; # 再次读,读取到了B事物更新的数据,保证了在一次事物中,数据的可重复读 SELECT * FROM USER; ``` ```mysql # 开启B事物 BEGIN; UPDATE USER SET NAME="rr"; # 提交 COMMIT; ``` #### 幻读问题 前面的脏读和不可重复读都好理解,但是“幻读”就不太好理解了。参考博客: https://segmentfault.com/a/1190000016566788?utm_source=tag-newest 以下为摘抄的博文: > 幻读错误的理解:说幻读是 事务A 执行两次 select 操作得到不同的数据集,即 select 1 得到 10 条记录,select 2 得到 11 条记录。这其实并不是幻读,这是不可重复读的一种,只会在 R-U R-C 级别下出现,而在 mysql 默认的 RR 隔离级别是不会出现的。 笔者也亲自测试过,幻读理解确实是这样,而且在RR隔离级别下是不会出现这种情况的。 这里给出我对幻读的比较白话的理解: > 幻读,并不是说两次读取获取的结果集不同,幻读侧重的方面是某一次的 select 操作得到的结果所表征的数据状态无法支撑后续的业务操作。更为具体一些:select 某记录是否存在,不存在,准备插入此记录,但执行 insert 时发现此记录已存在,无法插入,此时就发生了幻读。 实验 开始实验前的数据环境 ```mysql CREATE TABLE `user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(200) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 ``` 现有数据: ```mysql mysql> SELECT * FROM USER; +----+------+ | id | name | +----+------+ | 1 | rr | | 2 | rr | | 3 | rr | | 4 | Jacy | +----+------+ ``` 事物A ```mysql # 开启事物 begin; # 查询是否有Id为5的一条数据 执行命令的时候是2020-05-17 11:25:32 select * from user where id =5; # 结果为空 Empty set (0.00 sec) # 往user表中插入id为5的一条数据 执行命令的时候是2020-05-17 11:25:35 insert into user(id,name) values(5,"susan") # 结果 主键重复 ERROR 1062 (23000): Duplicate entry '5' for key 'PRIMARY' # 再次查询 select * from user where id =5; # 结果还是空 A事物就发生幻读现象了 Empty set (0.00 sec) commit; ``` 事物B ```mysql # 开启事物 begin; # 往user表中插入id为5的一条数据 2020-05-17 11:25:33 insert into user(id,name) values(5,"susan") # 提交事物 2020-05-17 11:25:34 commit; ``` #### 可重复读下解决幻读 RR级别下只要对 SELECT 操作也手动加行(X)锁即可类似 SERIALIZABLE 级别(它会对 SELECT 隐式加锁),即大家熟知的: ```mysql #这里需要用 X锁, 用 LOCK IN SHARE MODE 拿到 S锁 后我们没办法做 写操作 SELECT `id` FROM `users` WHERE `id` = 6 FOR UPDATE; ``` 事物A ```mysql # 开启事物 begin; # 手动加X锁 执行命令的时候是2020-05-17 11:35:32 select * from user where id =5 for update; # 结果为空 Empty set (0.00 sec) # 往user表中插入id为5的一条数据 执行命令的时候是2020-05-17 11:35:35 insert into user(id,name) values(6,"jocy") commit; ``` 事物B ```mysql # 开启事物 begin; # 往user表中插入id为5的一条数据 2020-05-17 11:35:33 insert into user(id,name) values(6,"jocy") # #一直会阻塞 # #A事物提交后,解除阻塞,报主键重复的错 ERROR 1062 (23000): Duplicate entry '6' for key 'PRIMARY' commit; ```