最后
====================================================================
ACID是一个久远的说法。吉姆·格雷(Jim Gray)在我出生之前就已经描述了原子性,一致性和持久性。但是那篇论文没有提到隔离性。如果我们想到70年代后期的数据库管理系统,这是可以理解的,吉姆·格雷说:
“目前,最大的航空公司和银行在任何时刻都有大约10,000个操作和大约100个活跃的事务”。
因此,主要的付出都花在保证交付正确性上,而不是并发上。从那个时候到现在,情况发生了翻天覆地的变化,如今,即使是较低的设置也有1000 TPS。
从数据库的角度来看,原子性是固定属性,但是出于性能/可伸缩性的考虑,其它的特性都需要权衡。
如果数据库系统由多个节点组成,则分布式系统一致性(CAP定理中的C,而不是ACID中的C)要求将所有更改都同步到所有节点(多主从复制)。如果副本节点是异步更新的,那么我们将违反一致性规则,系统将“最终保持一致”。
彼得·贝利斯(Peter Bailis)的一篇很好的文章解释了CAP定理中的一致性和ACID中的一致性之间的区别。
事务是数据状态转换,因此即使所有事务同时执行,系统也必须像所有事务都是以串行形式发生一样进行操作。
如果始终只有一个连接运行,那么串行将不会增加任何并发控制成本。实际上,所有事务系统都必须兼容并发请求,因此序列化会影响可伸缩性。阿姆达尔定律描述了串行执行与并发之间的关系:
“在并行计算中使用多个处理器的程序的速度受到程序顺序部分所需时间的限制。”
稍后你将看到,大多数数据库管理系统都选择(默认情况下)放宽数据正确性的要求,以实现更好的并发性。
如果企业系统业务需求不要求持久性事务,那么对于高性能集群数据库来说,持久性发挥作用才有意义。但是,大多数情况下,持久性最好保持不变。
======================================================================
尽管某些数据库管理系统提供了MVCC,但通常并发控制是通过锁来实现的。但是众所周知,锁会增加执行代码的可序列化部分,从而影响并行效率。
SQL标准定义了四个隔离级别:
-
读取未提交(READ_UNCOMMITTED)
-
读取已提交(READ_COMMITTED)
-
可重复读(REPEATABLE_READ)
-
串行化(Serializable)
除串行化级别外,其他所有级别都可能受到数据异常的影响,不同级别可能发生的数据异常现象如下:
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
| — | — | — | — |
| 读取未提交 | 允许 | 允许 | 允许 |
| 读取已提交 | 阻止 | 允许 | 允许 |
| 可重复读 | 阻止 | 阻止 | 允许 |
| 串行化 | 阻止 | 阻止 | 阻止 |
======================================================================
但是,我们刚刚列出的所有异常现象是什么?我们对每一个进行讨论。
当允许事务读取其他正在运行的事务的未提交更改时,就会发生脏读。发生这种情况是因为没有锁阻止它。在上图中,您可以看到第二个事务使用了不一致的值,因为第一个事务已回滚。
有关“脏读”异常的更多信息,请查看这篇文章。
所谓不可重复读,由于并发事务刚刚更新了我们正在读取的记录,因此连续读取产生不同的结果。这是我们不想要的,因为最终使用了过时的数据。通过在当前事务的整个持续时间内在读取记录上保留一个共享锁(读锁),可以防止这种情况。
有关不可重复读取异常的更多信息,请查看这篇文章。
当后续的事务插入了数据,刚好插入的数据又能被并行的事务先前的查询查到,就会发生幻读。因此,我们最终将使用过时的数据,这可能会影响我们的业务运行。使用范围锁或谓词锁可以防止这种情况。
有关幻读异常的更多信息,请查看这篇文章。
即使在SQL标准中未提及,你也应注意其它的现象,例如:
-
丢失更新
-
读取偏差
-
写入偏差
知道何时会发生这些现象就可以正确地解决它们,这就是数据完整性的全部意义所在。
========================================================================
即使SQL标准要求使用SERIALIZABLE隔离级别,但大多数数据库管理系统使用不同的默认级别。
| 数据库 | 隔离级别 |
| — | — |
| Oracle | 读取已提交 |
| MySQL | 可重复读 |
| Microsoft SQL Server | 读取已提交 |
| PostgreSQL | 读取已提交 |
| DB2 | CURSOR STABILITY |
====================================================================
通常,READ COMMITTED是合适的选择,因为即使SERIALIZABLE都不能保护你免受丢失更新的影响,在更新丢失的情况下,读/写发生在不同的事务(和Web请求)中。你应该考虑你的系统要求,并进行测试以确定哪个隔离级别最适合你的需求。
博主水平有限,难免错漏,欢迎指出,或直接查看原文!
最后
分享一些系统的面试题,大家可以拿去刷一刷,准备面试涨薪。
这些面试题相对应的技术点:
- JVM
- MySQL
- Mybatis
- MongoDB
- Redis
- Spring
- Spring boot
- Spring cloud
- Kafka
- RabbitMQ
- Nginx
- …
大类就是:
- Java基础
- 数据结构与算法
- 并发编程
- 数据库
- 设计模式
- 微服务
- 消息中间件
[外链图片转存中…(img-RVZOTVKi-1715605582324)]
[外链图片转存中…(img-imqCpWcj-1715605582324)]