「读书」数据密集型应用 Day 12

107 阅读4分钟

事务

事务的棘手概念

事实并⾮如此简单:与其他技术设计选择⼀样,事务有其优势和局限性。为了理解这些权衡,让我们了解事务所提供保证的细节——⽆论是在正常运⾏中还是在各种极端(但是现实存在)情况下。

ACID 的含义

ACID代表原⼦性(Atomicity),⼀致性(Consistency),隔离性(Isolation)和持久性(Durability)。

原子性

能够在错误时中⽌事务,丢弃该事务进⾏的所有写⼊变更的能⼒。

一致性

对数据的⼀组特定陈述必须始终成⽴

隔离性

同时执⾏的事务是相互隔离的:它们不能相互冒犯。传统的数据库教科书将隔离性形式化为可序列化(Serializability),这意味着每个事务可以假装它是唯⼀在整个数据库上运⾏的事务。

持久性

持久性 是⼀个承诺,即⼀旦事务成功完成,即使发⽣硬件故障或数据库崩溃,写⼊的任何数据也不会丢失。

单对象和多对象操作

单对象写入

事务通常被理解为,将多个对象上的多个操作合并为⼀个执⾏单元的机制。

多对象事务的需求

这些应⽤仍然可以在没有事务的情况下实现。然⽽,没有原⼦性,错误处理就要复杂得多,缺乏隔离性,就会导致并发问题。

处理错误和中止

弱隔离级别

出于这个原因,数据库⼀直试图通过提供事务隔离(transaction isolation) 来隐藏应⽤程序开发者的并发问题。从理论上讲,隔离可以通过假装没有并发发⽣,让你的⽣活更加轻松:可序列化(serializable) 的隔离等级意味着数据库保证事务的效果与连续运⾏(即⼀次⼀个,没有任何并发)是⼀样的。 实际上不幸的是:隔离并没有那么简单。可序列化 会有性能损失,许多数据库不愿意⽀付这个代价。因此,系统通常使⽤较弱的隔离级别来防⽌⼀部分,⽽不是全部的并发问题。这些隔离级别难以理解,并且会导致微妙的错误,但是它们仍然在实践中被使⽤。

读已提交

没有脏读

设想⼀个事务已经将⼀些数据写⼊数据库,但事务还没有提交或中⽌。另⼀个事务可以看到未提交的数据吗?如果是的话,那就叫做脏读(dirty reads)

没有脏写

但是,如果先前的写⼊是尚未提交事务的⼀部分,⼜会发⽣什么情况,后⾯的写⼊会覆盖⼀个尚未提交的值?这被称作脏写(dirty write)。

实现读已提交

快照隔离和可重复读

实现快照隔离

与读取提交的隔离类似,快照隔离的实现通常使⽤写锁来防⽌脏写(请参阅“读已提交”),这意味着进⾏写⼊的事务会阻⽌另⼀个事务修改同⼀个对象。但是读取不需要任何锁定。从性能的⻆度来看,快照隔离的⼀个关键原则是:读不阻塞写,写不阻塞读。这允许数据库在处理⼀致性快照上的⻓时间查询时,可以正常地同时处理写⼊操作。且两者间没有任何锁定争⽤。

观察⼀致性快照的可⻅性规则

当⼀个事务从数据库中读取时,事务ID⽤于决定它可以看⻅哪些对象,看不⻅哪些对象。通过仔细定义可⻅性规则,数据库可以向应⽤程序呈现⼀致的数据库快照。

索引和快照隔离

⼀种选择是使索引简单地指向对象的所有版本,并且需要索引查询来过滤掉当前事务不可⻅的任何对象版本。当垃圾收集删除任何事务不再可⻅的旧对象版本时,相应的索引条⽬也可以被删除。

可重复读和命名混淆