diff --git a/ch7.md b/ch7.md index 3cb0660..9885cc2 100644 --- a/ch7.md +++ b/ch7.md @@ -462,7 +462,7 @@ UPDATE wiki_pages SET content = '新内容' 另一方面,最后写入胜利(LWW)的冲突解决方法很容易丢失更新,如 “[最后写入胜利(丢弃并发写入)](ch5.md#最后写入胜利(丢弃并发写入))” 中所述。不幸的是,LWW 是许多复制数据库中的默认方案。 -### 写入偏斜与幻读 +### 写入偏差与幻读 前面的章节中,我们看到了 **脏写** 和 **丢失更新**,当不同的事务并发地尝试写入相同的对象时,会出现这两种竞争条件。为了避免数据损坏,这些竞争条件需要被阻止 —— 既可以由数据库自动执行,也可以通过锁和原子写操作这类手动安全措施来防止。 @@ -478,13 +478,13 @@ UPDATE wiki_pages SET content = '新内容' 在两个事务中,应用首先检查是否有两个或以上的医生正在值班;如果是的话,它就假定一名医生可以安全地休班。由于数据库使用快照隔离,两次检查都返回 2 ,所以两个事务都进入下一个阶段。Alice 更新自己的记录休班了,而 Bob 也做了一样的事情。两个事务都成功提交了,现在没有医生值班了。违反了至少有一名医生在值班的要求。 -#### 写偏差的特征 +#### 写入偏差的特征 -这种异常称为 **写偏差**【28】。它既不是 **脏写**,也不是 **丢失更新**,因为这两个事务正在更新两个不同的对象(Alice 和 Bob 各自的待命记录)。在这里发生的冲突并不是那么明显,但是这显然是一个竞争条件:如果两个事务一个接一个地运行,那么第二个医生就不能歇班了。异常行为只有在事务并发进行时才有可能发生。 +这种异常称为 **写入偏差**【28】。它既不是 **脏写**,也不是 **丢失更新**,因为这两个事务正在更新两个不同的对象(Alice 和 Bob 各自的待命记录)。在这里发生的冲突并不是那么明显,但是这显然是一个竞争条件:如果两个事务一个接一个地运行,那么第二个医生就不能歇班了。异常行为只有在事务并发进行时才有可能发生。 可以将写入偏差视为丢失更新问题的一般化。如果两个事务读取相同的对象,然后更新其中一些对象(不同的事务可能更新不同的对象),则可能发生写入偏差。在多个事务更新同一个对象的特殊情况下,就会发生脏写或丢失更新(取决于时序)。 -我们已经看到,有各种不同的方法来防止丢失的更新。但对于写偏差,我们的选择更受限制: +我们已经看到,有各种不同的方法来防止丢失的更新。但对于写入偏差,我们的选择更受限制: * 由于涉及多个对象,单对象的原子操作不起作用。 * 不幸的是,在一些快照隔离的实现中,自动检测丢失更新对此并没有帮助。在 PostgreSQL 的可重复读,MySQL/InnoDB 的可重复读,Oracle 可串行化或 SQL Server 的快照隔离级别中,都不会自动检测写入偏差【23】。自动防止写入偏差需要真正的可串行化隔离(请参阅 “[可串行化](#可串行化)”)。 @@ -507,9 +507,9 @@ COMMIT; * 和以前一样,`FOR UPDATE` 告诉数据库锁定返回的所有行以用于更新。 -#### 写偏差的更多例子 +#### 写入偏差的更多例子 -写偏差乍看像是一个深奥的问题,但一旦意识到这一点,很容易会注意到它可能发生在更多场景下。以下是一些例子: +写入偏差乍看像是一个深奥的问题,但一旦意识到这一点,很容易会注意到它可能发生在更多场景下。以下是一些例子: * 会议室预订系统 @@ -771,7 +771,7 @@ WHERE room_id = 123 AND #### 基于过时前提的决策 -先前讨论了快照隔离中的写入偏差(请参阅 “[写入偏斜与幻读](#写入偏斜与幻读)”)时,我们观察到一个循环模式:事务从数据库读取一些数据,检查查询的结果,并根据它看到的结果决定采取一些操作(写入数据库)。但是,在快照隔离的情况下,原始查询的结果在事务提交时可能不再是最新的,因为数据可能在同一时间被修改。 +先前讨论了快照隔离中的写入偏差(请参阅 “[写入偏差与幻读](#写入偏差与幻读)”)时,我们观察到一个循环模式:事务从数据库读取一些数据,检查查询的结果,并根据它看到的结果决定采取一些操作(写入数据库)。但是,在快照隔离的情况下,原始查询的结果在事务提交时可能不再是最新的,因为数据可能在同一时间被修改。 换句话说,事务基于一个 **前提(premise)** 采取行动(事务开始时候的事实,例如:“目前有两名医生正在值班”)。之后当事务要提交时,原始数据可能已经改变 —— 前提可能不再成立。 @@ -849,7 +849,7 @@ WHERE room_id = 123 AND 两个客户端同时执行 **读取 - 修改 - 写入序列**。其中一个写操作,在没有合并另一个写入变更情况下,直接覆盖了另一个写操作的结果。所以导致数据丢失。快照隔离的一些实现可以自动防止这种异常,而另一些实现则需要手动锁定(`SELECT FOR UPDATE`)。 -* 写偏差 +* 写入偏差 一个事务读取一些东西,根据它所看到的值作出决定,并将该决定写入数据库。但是,写入时,该决定的前提不再是真实的。只有可串行化的隔离才能防止这种异常。