1月阅读周·MySQL数据库入门:事务的隔离级别之不可重复读篇
【摘要】 背景去年下半年,我在微信书架里加入了许多技术书籍,各种类别的都有,断断续续的读了一部分。没有计划的阅读,收效甚微。新年伊始,我准备尝试一下其他方式,比如阅读周。每月抽出1~2个非连续周,完整阅读一本书籍。这个“玩法”虽然常见且板正,但是有效,已经坚持阅读十二个月。已读完书籍:《架构简洁之道》、《深入浅出的Node.js》、《你不知道的JavaScript(上卷)》、《你不知道的JavaScr...
背景
去年下半年,我在微信书架里加入了许多技术书籍,各种类别的都有,断断续续的读了一部分。
没有计划的阅读,收效甚微。
新年伊始,我准备尝试一下其他方式,比如阅读周。每月抽出1~2个非连续周,完整阅读一本书籍。
这个“玩法”虽然常见且板正,但是有效,已经坚持阅读十二个月。
已读完书籍:《架构简洁之道》、《深入浅出的Node.js》、《你不知道的JavaScript(上卷)》、《你不知道的JavaScript(中卷)》、《你不知道的JavaScript(下卷)》、《数据结构与算法JavaScript描述》、《WebKit技术内幕》、《前端架构:从入门到微前端》、《秒懂算法:用常识解读数据结构与算法》、《JavaScript权威指南》、《JavaScript异步编程设计快速响应的网络应用》、《编写可测试的JavaScript代码》。
当前阅读周书籍:《MySQL数据库入门》。
事务的隔离级别
数据库是多线程并发访问的,所以很容易出现多个线程同时开启事务的情况,这样就会出现脏读、重复读以及幻读的情况,为了避免这种情况的发生,就需要为事务设置隔离级别。在MySQL中,事务有4种隔离级别,接下来将针对这4种隔离级别进行详细的讲解。
1.READ UNCOMMITTED
READ UNCOMMITTED(读未提交)是事务中最低的级别,该级别下的事务可以读取到另一个事务中未提交的数据,也被称为脏读(Dirty Read),这是相当危险的。由于该级别较低,在实际开发中避免不了任何情况,所以一般很少使用。
2.READ COMMITTED
大多数的数据库管理系统的默认隔离级别都是READ COMMITTED(读提交)(如Oracle),该级别下的事务只能读取其他事务已经提交的内容,可以避免脏读,但不能避免重复读和幻读的情况。重复读就是在事务内重复读取了别的线程已经提交的数据,但两次读取的结果不一致,原因是查询的过程中其他事务做了更新的操作。幻读是指在一个事务内两次查询中数据条数不一致,原因是查询的过程中其他的事务做了添加操作。这两种情况并不算错误,但有些情况是不符合实际需求的,后面会具体讲解。
3.REPEATABLE READ
REPEATABLE READ(可重复读)是MySQL默认的事务隔离级别,它可以避免脏读、不可重复读的问题,确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。但理论上,该级别会出现幻读的情况,不过MySQL的存储引擎通过多版本并发控制机制解决了该问题,因此该级别是可以避免幻读的。
4.SERIALIZABLE
SERIALIZABLE(可串行化)是事务的最高隔离级别,它会强制对事务进行排序,使之不会发生冲突,从而解决脏读、幻读、重复读的问题。实际上,就是在每个读的数据行上加锁。这个级别,可能导致大量的超时现象和锁竞争,实际应用中很少使用。
上述的4种级别可能会产生不同的问题,如脏读、重复读、幻读、耗时的操作等。
接下来着重介绍不可重复读。
不可重复读
所谓的不可重复读(NON-REPEATABLE READ)是指事务中两次查询的结果不一致,原因是在查询的过程中其他事务做了更新的操作。例如,银行在做统计报表时,第一次查询a账户有1000元钱,第二次查询a账户有900元钱,原因是统计期间a账户取出了100元,这样就会导致多次统计报表的结果不一致。
不可重复读和脏读有点类似,但是脏读是读取前一个事务未提交的脏数据,不可重复读是在事务内重复读取了别的线程已提交的数据,对于初学者来说可能比较难以理解,接下来就通过案例来演示不可重复读的情况,具体步骤如下。
1、演示不可重复读
b账户:首先在b账户中开启一个事务,然后在当前事务中查询各账户的余额信息,查询结果如下:
mysql>START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql>select * from account;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 1000 |
| 2 | b | 1000 |
+----+------+-------+
2 rows in set (0.00 sec)
a账户:在a账户中不用开启事务,直接使用UPDATE语句执行更新操作即可,具体语句如下:
UPDATE account SET money=money-100 WHERE name ='a';
由于a账户只需要执行修改的操作,不需要保证同步性,因此直接执行SQL语句就可以,执行结果如下所示:
mysql>UPDATE account SET money=money-100 WHERE name ='a';
Query OK, 1 row affected (0.02 sec)
Rows matched: 1 Changed: 1 Warnings: 0
使用SELECT语句查询a账户的余额信息,查询结果如下:
mysql>SELECT * FROM account;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 900 |
| 2 | b | 1000 |
+----+------+-------+
2 rows in set (0.00 sec)
b账户:当a账户中的更新操作执行成功后,在b账户中再次查询各账户的余额,查询结果如下:
mysql>SELECT * FROM account;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 900 |
| 2 | b | 1000 |
+----+------+-------+
2 rows in set (0.00 sec)
对比b账户两次查询结果可以发现,两次查询结果是不一致的,实际上这种操作是没错的,但是如果在银行统计报表时,这种情况是不符合需求的,因为我们并不希望在一个事务中看到的查询结果不一致,这就是不可重复读。上述情况演示成功后,还是要将b账户中的事务提交。
2、设置b账户中事务的隔离级别
b账户:为了防止重复读的情况出现,可以将该事务的隔离级别设置为REPEATABLE READ(可重复读),具体语句如下:
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
上述语句执行成功后,b账户事务的隔离级别被设置为REPEATABLE READ。
3、验证是否出现不可重复读
b账户:在b账户中,重新开启一个事务,然后使用SELECT语句查询当前账户的余额,查询结果如下:
mysql>START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql>SELECT * FROM account;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 900 |
| 2 | b | 1000 |
+----+------+-------+
2 rows in set (0.00 sec)
a账户:在a账户中不开启事务,直接使用UPDATE语句执行更新操作,具体如下:
UPDATE account SET money=money-100 WHERE name='a';
使用SELECT语句查询各账户的余额信息,查询结果如下:
mysql>SELECT * FROM account;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 800 |
| 2 | b | 1000 |
+----+------+-------+
2 rows in set (0.00 sec)
b账户:当a账户中的UPDATE语句执行成功后,b账户在当前事务中,再次查询各账户的余额信息,查询结果如下:
mysql>SELECT * FROM account;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 900 |
| 2 | b | 1000 |
+----+------+-------+
2 rows in set (0.00 sec)
对比b账户两次的查询结果可以发现,查询的结果是一致的,并没有出现不同的数据,因此,可以说明事务的隔离级别为REPEATABLE READ时,可以避免重复读的情况。演示完成后,将b账户中的事务提交。
总结
数据库是多线程并发访问的,所以很容易出现多个线程同时开启事务的情况,这样就会出现脏读、重复读以及幻读的情况,为了避免这种情况的发生,就需要为事务设置隔离级别。在MySQL中,事务有4种隔离级别,分别是READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ、SERIALIZABLE。
这4种级别可能会产生不同的问题,如脏读、重复读、幻读、耗时的操作等。
所谓的不可重复读(NON-REPEATABLE READ)是指事务中两次查询的结果不一致,原因是在查询的过程中其他事务做了更新的操作。
作者介绍
非职业「传道授业解惑」的开发者叶一一。
《趣学前端》、《CSS畅想》等系列作者。华夏美食、国漫、古风重度爱好者,刑侦、无限流小说初级玩家。
如果看完文章有所收获,欢迎点赞👍 | 收藏⭐️ | 留言📝。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
作者其他文章
评论(0)