通过 前一章 [ 28-MySQL 多事务并发执行的数据一致性问题 ] 的讨论,可能会遇到的几种问题,包括脏写、脏读、不可重复读和幻读。这些问题的根本原因是由于多个事务之间的数据竞争,导致事务间没有适当的隔离。
为了避免这些并发问题,SQL标准规定了几种事务隔离级别。需要注意的是,讨论的是SQL标准中的事务隔离级别,而不是MySQL的具体实现。MySQL在实现这些隔离级别时可能会有一些不同,我们将在后面详细探讨。
SQL标准中规定了四种事务隔离级别,这四种级别决定了多个并发事务如何隔离,从而减少并发问题的发生。它们分别是:
1. Read Uncommitted(读未提交-RU):
在这个级别下,脏写是允许的。意味着两个事务在未提交时,能同时修改同一行数据。这个级别可能会导致脏写、脏读、不可重复读和幻读 所有并发修改的异常问题。这种隔离级别对数据库的并发控制非常松散,可以说几乎没有并发控制,一般情况下不推荐使用。
2. Read Committed(读已提交-RC):
[ 重点 ]
在这个级别下,脏写和脏读是不会发生的。也就是说,你不会看到其他事务尚未提交的修改。但不可重复读和幻读问题仍然可能发生。因为一旦其他事务提交了数据,当前事务会读取到这些提交的数据,导致多次查询的结果可能不一致。该级别常见的缩写是RC,如果在团队中讨论时听到别人提到“把事务隔离级别设置成RC”,意味着使用的是“读已提交”级别。
3. REPEATABLE READ(可重复读-RR):
[ 重点 by the way mysql 事务默认隔离级别 并且 mysql 在 RR 解决了 幻读 ]
在这个级别下,脏写、脏读、不可重复读是不会发生的。具体来说,当事务A开始执行后,即使其他事务修改了数据并提交,事务A仍然只能读取到它开始时的数据。这意味着,如果事务A在执行过程中多次查询同一数据,它每次都会得到相同的值,而不会受到其他事务提交的影响。因此,事务A在执行期间读取到的值始终保持一致。REPEATABLE READ 隔离级别就确保了事务A在整个执行期间,查询到的同一数据值是一致的,不会受到其他事务提交的修改影响。
4. SERIALIZABLE(串行化):
这种级别是最严格的,基本上不允许多个事务并发执行。所有事务必须按照顺序执行:事务A先执行并提交,然后是事务B,最后是事务C,依此类推。
由于事务都是串行执行的,所以自然不存在并发的情况,幻读和其他事务并发问题根本无法发生。因为事务间没有交集,每个事务都是独立执行的。
然而,SERIALIZABLE级别的性能非常差,极少在实际开发中使用。原因很简单,事务必须串行执行,可能导致数据库的并发性能极为低下,尤其是在高并发的场景下,性能几乎无法接受。
5. 总结
SQL 标准中定义了四种事务隔离级别,每个隔离级别对并发事务的控制程度不同,因此解决脏读、脏写、不可重复读和幻读的能力也不同。
SERIALIZABLE是最高级别的事务隔离,但由于它对性能的巨大影响,除非极少数特殊场景,一般很少使用。
RC(读已提交)和RR(可重复读)是最常见的,大家在日常开发中会更常用到这两种隔离级别。
下面是一个表格,列出了不同事务隔离级别能解决的并发问题:
解释:
脏读:
脏读发生在事务 A 读取了事务 B 未提交的数据。即事务 A 可能会读取到事务 B 中间状态的数据,这些数据在事务 B 回滚时会消失。
解决能力:从“读已提交”隔离级别开始,事务 A 只能读取事务 B 已提交的数据,因此不再出现脏读。
脏写:
脏写发生在事务 A 修改了事务 B 尚未提交的数据,这种修改可能会被事务 B 回滚,导致事务 A 做出的修改是无效的。
解决能力:只有 串行化 隔离级别能完全避免脏写。在该级别下,事务 A 和事务 B 完全串行执行。
不可重复读:
不可重复读 发生在事务 A 读取了某个数据项,事务 B 在事务 A 期间修改了该数据项,再次读取时,事务 A 读到的数据就发生了变化。
解决能力:从“可重复读”开始,事务 A 在整个事务过程中读取的数据保持一致,不会因其他事务修改而变化,避免了不可重复读。
幻读:
幻读发生在事务 A 读取了某个范围的数据,事务 B 在事务 A 期间插入或删除了数据,使得事务 A 在第二次读取时,得到的数据集与第一次读取时不同。
解决能力:串行化 隔离级别解决了幻读问题,确保事务 A 在执行期间其他事务不能插入或删除满足查询条件的数据。
总结:
读未提交:不解决任何问题,所有并发问题都可能发生。
读已提交:避免脏读,但可能会有脏写、不可重复读和幻读。
可重复读:避免脏读和不可重复读,但可能会有幻读。
串行化:解决所有并发问题,包括脏读、脏写、不可重复读和幻读。
评论区