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

han*_*ora 7 database acid isolation-level

维基说;

可重复读取:
在此隔离级别中,基于锁的并发控制DBMS实现保持读取和写入锁定(在选定数据上获取),直到事务结束.但是,不管理范围锁定,因此可能会发生幻像读取.

在这种隔离级别可以进行写入偏移,这种现象是两个不同的写入者(他们之前已经读过他们正在更新的列)允许两个写入表中的同一列,从而导致列具有的数据是这两笔交易的混合.

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

hqt*_*hqt 13

可重复读取隔离级别可确保每个事务都从数据库的一致快照读取.换句话说,在同一事务中检索两次行总是具有相似的值.

Postgres,SQLServer等可重复读隔离级别的数据库可以检测丢失的更新(写偏斜的特殊情况),但有些则没有.(即:MySQL中的InnoDB)

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

我以设计数据密集型应用程序为例.这是场景:

您正在为医生编写申请,以管理他们在医院的随叫随到的轮班.医院通常会在任何时候试图让几位医生随叫随到,但绝对必须至少有一位医生随叫随到.医生可以放弃轮班(例如,如果他们自己生病了),前提是至少有一位同事留在那个班次

那么我们如何在数据库下实现这一点.这是伪代码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;  
Run Code Online (Sandbox Code Playgroud)

这是插图: 流量数据

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

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

  1. 锁定所有正在通话的记录.因此Bob或Alice将等到其他完成交易.

这是使用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;  
Run Code Online (Sandbox Code Playgroud)
  1. 使用更严格的隔离级别.双方的MySQL,Postgres的 T-SQL提供了序列化隔离级别.

  • 继续我之前的评论:实际上你写的一切都是对的。在 MySQL 中的可序列化隔离级别的情况下,假设事务语句的交错与示例中相同,两个事务都将在每个医生记录上获取“S”锁,然后两个事务都会评估“current_on_call >= 2”语句,结果为“true” ,那么两个事务都将被阻止,因为执行“Update”语句时它们需要“X”锁,但它们无法获取它,因为另一个事务在所有医生记录上持有“S”锁。结果——死锁。一笔事务将被回滚 - 防止写入偏差 (3认同)