事务在当今的企业系统中无处不在,即使在高度并发的环境中也可以提供数据完整性。 因此,让我们首先定义术语和上下文(通常可以使用该术语)。
事务是读/写操作的集合,仅当所有包含的操作都成功时才成功。
事务本身具有四个属性(通常称为ACID):
- 原子性
- 一致性
- 隔离
- 耐用性
了解这些内容非常重要,因此我们将分别讨论以下内容。
1.原子性接受单个操作并将其转变为一项全有或全无的工作单元,只有当所有包含的操作都成功时,该操作才能成功。
2.事务可能封装状态更改(除非它是只读的)。 事务在任何给定时间都必须始终保持系统处于一致状态,无论有多少个并发事务交织在一起。
一致性具有以下特点:
- 如果一个操作触发了辅助操作(CASCADE,TRIGGERS),则这些操作也必须成功,否则事务将失败。
- 如果系统由多个节点组成,则一致性要求将所有更改传播到所有节点( 多主复制 )。 如果从节点是异步更新的,那么我们将违反一致性规则,系统将“ 最终保持一致 ”。
- 事务是数据状态转换,因此即使所有事务同时执行,系统也必须像所有事务都以串行形式发生一样进行操作。
如果始终只有一个连接运行,那么可串行性将不会增加任何并发控制成本。 实际上,所有事务系统都必须容纳并发请求,因此序列化会影响可伸缩性。 阿姆达尔定律描述了串行执行与并发之间的关系:
“在并行计算中使用多个处理器的程序的速度受到程序顺序部分所需时间的限制。”
稍后您将看到,大多数数据库管理系统都选择(默认情况下)放松一致性,以实现更好的并发性。
3.隔离
事务是并发控制机制,即使交织,它们也可以提供一致性。 隔离为我们带来了隐藏外界未提交的状态更改的好处,因为失败的事务永远都不会破坏系统的状态。 通过使用悲观或乐观锁定机制的并发控制来实现隔离。
4.耐久性
成功的事务必须永久更改系统的状态,并且在结束系统之前,将状态更改记录在持久的事务日志中 。 如果我们的系统突然受到系统崩溃或断电的影响,那么所有未完成的已提交事务都可能会被重放。
对于JMS之类的消息传递系统,事务不是必需的。 这就是为什么我们有非事务确认模式 。
文件系统操作通常是非托管的,但是如果您的业务需求需要事务文件操作,则可以使用XADisk之类的工具。
虽然,消息传递和文件系统可选地使用事务,但对于数据库管理系统,事务是强制性的。 这就是数据库连接定义默认自动提交模式的原因。 ACID是SQL标准规定的,因此所有操作都必须嵌入数据库事务中。 但是对于企业应用程序,通常会避免使用自动提交模式,因为它的性能很差,并且不允许您在单个原子工作单元中包含多个DML操作。
酸是旧学校。 吉姆·格雷 ( Jim Gray)在我出生之前就已经描述了原子性,一致性和耐用性。 但是那篇论文没有提到隔离。 如果我们想到70年代后期的生产系统,这是可以理解的,根据Jim Gray的说法:
“目前,最大的航空公司和银行在任何时刻都有大约10,000个航站楼和大约100笔活跃交易”。
因此,所有的努力都花在交付一致性而不是并发上。 从那时起,情况发生了翻天覆地的变化,如今,即使是适度的设置也能够运行1000 TPS。
从数据库的角度来看,原子性是固定属性,但是出于性能/可伸缩性的原因,其他所有事物都可以进行权衡。
如果企业系统业务要求不要求持久性事务,那么对高性能集群数据库来说,持久性发挥作用才有意义。 但是,大多数情况下,耐用性最好保持不变。
尽管某些数据库管理系统提供了MVCC ,但通常并发控制是通过锁定来实现的。 但是众所周知,锁定会增加执行代码的可序列化部分,从而影响并行化 。
SQL标准定义了四个隔离级别:
- READ_UNCOMMITTED
- READ_COMMITTED
- REPETABLE_READ
- 可序列化
除SERIALIZABLE级别之外的所有级别都受到数据异常(现象)的影响,该异常可能按照以下模式发生:
| 隔离度 | 脏读 | 不可重复读 | 幻影阅读 |
|---|---|---|---|
| READ_UNCOMMITTED | 允许的 | 允许的 | 允许的 |
| READ_COMMITTED | 预防 | 允许的 | 允许的 |
| REPETABLE_READ | 预防 | 预防 | 允许的 |
| 可序列化 | 预防 | 预防 | 预防 |
但是,我们将列出所有这些现象。 让我们讨论其中的每一个。
1.脏读
当允许事务读取其他正在运行的事务的未提交更改时,就会发生脏读。 发生这种情况是因为没有锁定阻止它。 在上图中,您可以看到第二个事务使用的值与第一个事务已回滚时的值不一致。
2.不可重复读
当连续读取由于刚更新我们正在读取的记录的并发事务而产生不同的结果时,将显示非重复读取。 这是不希望的,因为我们最终使用了过时的数据。 在当前事务的整个持续时间内,通过在读取记录上保留一个共享锁(读取锁)可以防止这种情况。
3.幻影阅读
当第二笔交易插入与第一笔交易的先前选择条件匹配的行时,就会发生幻像读取。 因此,我们最终使用了过时的数据,这可能会影响我们的业务运营。 使用范围锁或谓词锁可以防止这种情况。
即使SOL标准要求使用SERIALIZABLE隔离级别,大多数数据库管理系统仍使用不同的默认级别。
| 数据库 | 默认隔离级别 |
|---|---|
| 甲骨文 | READ_COMMITTED |
| 的MySQL | REPETABLE_READ |
| Microsoft SQL服务器 | READ_COMMITTED |
| PostgreSQL的 | READ_COMMITTED |
| DB2 | 游标稳定性(又名READ_COMMITTED) |
通常,READ_COMMITED是正确的选择,因为即使SERIALIZABLE都不能保护您免受“丢失的更新”的影响,在这种情况下,读/写发生在不同的事务(和Web请求)中。 您应该考虑您的企业系统要求,并进行测试以确定哪个隔离级别最适合您的需求。
翻译自: https://www.javacodegeeks.com/2014/01/a-beginners-guide-to-acid-and-database-transactions.html