• T-SQL 重复读(Double Read)问题的理解


    我的理解是:

      step1,假设表里有100行有序记录, 事务1从row 1 开始读取到了row 50 并准备继续读取完这100行。

      要注意的是,sql server 会自动释放已经读取了的row的锁。

      step2,这时候,另外一个事务2 修改了 事务1已经读取并且被 sql server 释放掉锁的前面50行中的某些数据。

      这修改操作导致了数据排序发生变化,可能原来已经读取了的第20行现在排到了50行后面去。

      step3,然后,事务1继续读取剩下的数据。 就可能发现有数据被重复读取出来了。

    demo如下:

      1. 建立表和填充数据。

    CREATE TABLE dbo.testDoubleRead
    (
    id int identity(10,1) PRIMARY KEY,
    text_asc nvarchar(20)
    )
    go
    
    CREATE NONCLUSTERED INDEX IX_testDoubleRead_text_asc ON dbo.testDoubleRead(text_asc ASC);
    
    INSERT INTO dbo.testDoubleRead (text_asc)
    VALUES('A'),('B'),('C'),('D')

    注意非聚集索引的排序是 ASC,这时候表里的数据是:

    SELECT * FROM dbo.testDoubleRead
    
    /*
    
    id    text_asc
    10    A
    11    B
    12    C
    13    D
    
    */

      2. 开一个新session,执行下面这段不完整的事务:

    --SESSION 1 SCRIPT
    BEGIN TRANSACTION
    
    UPDATE dbo.testDoubleRead
    SET text_asc = 'UPDATE_C'
    WHERE id = 12

      3. 开另外一个session, 执行下面的查询:

    SELECT * FROM dbo.testDoubleRead

      显然,这查询是会被session1 block住的。

      

      4. 回到session1, 执行完毕如下代码:

    UPDATE dbo.testDoubleRead
    SET text_asc = 'UPDATE_B_2'
    WHERE id = 11
    
    COMMIT TRANSACTION

      5. 这时候, session2 会查询完毕,结果如下:

    SELECT * FROM dbo.testDoubleRead
    
    /*
    id    text_asc
    10    A
    11    B
    13    D
    11    UPDATE_B_2
    12    UPDATE_C
    */

      可以看到,有两条 id = 11的记录!

    再次分析下:

      1。按照 非聚集索引的定义,刚开始的数据应该是这样的

    /*
    id    text_asc
    10    A
    11    B
    12    C
    13    D
    */

      2. session1 开启事务并修改了id=12 的行,但没有提交。 这时候,session2 尝试读取数据 但只能读取到 id=11的,不能再朝下读取了;除非session1 释放id=12 的行。

      3. 接上面,session1 把id=12 的行修改了,根据非聚集索引的排序规则 text_asc ASC,可以确定值‘UPDATE_C’是应该排到最后一行的。 而且id=11的新值‘UPDATE_B’是在倒数第二行。

      理想数据的样子应该是这样的:

    /*
    id    text_asc
    10    A
    13    D
    11    UPDATE_B_2
    12    UPDATE_C
    */

      

      4.session修改完id=11以后就提交,这样session2就可以继续之前的查询了。注意的是,session2在被block之前已经读取了:  (id=11,text_asc='B')

      5.综合上面的几点,session2继续按照非聚集索引的顺序读取(id=13开始),而不会清空之前已经读取完毕的(id=11,text_asc='B')。

      所以最终的结果就是这样的了:

    /*
    id    text_asc
    10    A
    11    B    --在被session1的修改事务block之前读取了这行
    13    D    --session1提交事务以后,session2从这行继续读
    11    UPDATE_B_2
    12    UPDATE_C
    */

      

  • 相关阅读:
    数组
    对象
    js继承
    js原型原型链
    es6(初级)
    canvas背景
    Angular.js进阶
    Angular.js-2入门
    angular.js-1初识
    js之广告
  • 原文地址:https://www.cnblogs.com/SeraphWU/p/4219922.html
Copyright © 2020-2023  润新知