为什么在Repeatable读取中会发生写入偏斜?

时间:2018-01-24 08:12:29

标签: database isolation-level acid

Wiki说;

  

可重复阅读:
  在这个隔离级别,基于锁定   并发控制DBMS实现保持读写锁   (根据所选数据获得)直到交易结束。然而,   范围锁不受管理,因此可以进行幻像读取。

     

在此隔离级别可能会出现写入偏斜,这是一种现象,即两种情况   写入允许两个不同的表中的相同列   作者(以前读过他们正在更新的专栏),   导致列具有两者混合的数据   交易。

我很好奇为什么write skew Repeatable reads会发生? 它说它将保持读取和写入锁定直到事务结束,而write skew发生在previously read the columns they are updating时,那么当读取锁被锁定时如何锁定写锁定?

1 个答案:

答案 0 :(得分:10)

可重复的读取隔离级别确保每个事务将从数据库consistent snapshot中读取。换句话说,在同一事务中两次被检索的行始终具有相似的值。

可重复读取隔离级别的某些数据库(例如Postgres,SQLServer)可以检测到lost update(写偏斜的特殊情况),而另一些则不能。 (即:MySQL中的InnoDB)

回写偏斜问题。在某些情况下,大多数数据库引擎无法以可重复的读取隔离检测到。一种情况是 2个并发事务修改了 2个不同的对象并产生竞争条件。

我以Designing Data-Intensive Application为例。这是场景:

  

您正在为医生编写应用程序以管理他们的待命   在医院轮班。医院通常会尝试几个   任何时候都应召集医生,但绝对必须至少有   一位医生在召唤。医生可以放弃轮班(例如,如果他们   自己生病),但至少要有一名同事留在   接班

那么我们如何在数据库下实现它。这是伪代码sql代码:

BEGIN TRANSACTION;
    SELECT * FROM doctors
        WHERE on_call = true
        AND shift_id = 1234;
    if (current_on_call >= 2) {
        UPDATE doctors
        SET on_call = false WHERE name = 'Alice' AND shift_id = 1234;
    }
COMMIT;  

这是插图: Flow Data

如上图所示,我们看到Bob和Alice同时在SQL代码之上运行。但是Bob和Alice修改了不同的数据,Bob修改了Bob的记录,而Alice修改了Alice的记录。数据库处于可重复读取隔离级别,无法理解和检查条件(总医生> = 2)是否已被违反。写偏斜发生了。

要解决此问题,有两种方法:

  1. 锁定所有正在手动调用的记录。因此,鲍勃或爱丽丝都会等到其他人完成交易。

这里是使用SELECT .. FOR UPDATE查询的伪代码。

BEGIN TRANSACTION;
    SELECT * FROM doctors
        WHERE on_call = true
        AND shift_id = 1234 FOR UPDATE; // important here: locks all records that satisfied requirements.

    if (current_on_call >= 2) {
        UPDATE doctors
        SET on_call = false WHERE name = 'Alice' AND shift_id = 1234;
    }
  COMMIT;  
  1. 更严格地使用隔离级别。 MySQLPostgres T-SQL都提供序列化隔离级别。