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
*/

  

时间: 2024-10-26 16:35:55

T-SQL 重复读(Double Read)问题的理解的相关文章

关于数据库事务中脏读、不可重复读和幻读的理解

数据库有四种隔离级别,分别是: SOLATION_READ_UNCOMMITTED:允许读取改变了的还未提交的数据,可能导致脏读.不可重复读和幻读. ISOLATION_READ COMMITTED:允许并发事务提交之后读取,可以避免脏读,可能导致重复读和幻读. ISOLATION_REPEATABLE_READ:对相同字段的多次读取结果一致,可导致幻读. ISOLATION_SERIALIZABLE:完全服从ACID的原则,确保不发生脏读.不可重复读和幻读. 四种不同的隔离级别代表着数据库对资

[MySQL]对于事务并发处理带来的问题,脏读、不可重复读、幻读的理解与数据库事务隔离级别 - 分析脏读 & 不可重复读 & 幻读

刚开始写博客.. 写的太low. 1.数据库的两种读,每种读读的数据版本不一样,所以也称为MVCC,即多版本并发控制 a) 快照读 select * from where xxx  这种形式的都是快照读. b) 当前读 update , insert ,delete ,select xx from xx for update ,  in share mode 都是当前读 当前读会等待,不会返回数据的历史版本 2.mvcc 的实现原理 mvcc是基于read view.活跃事务列表 做的,以后的文

MySQL InnoDB四个事务级别 与 脏读、不重复读、幻读

MySQL InnoDB事务隔离级别脏读.可重复读.幻读 希望通过本文,可以加深读者对ySQL InnoDB的四个事务隔离级别,以及脏读.不重复读.幻读的理解. MySQL InnoDB事务的隔离级别有四级,默认是"可重复读"(REPEATABLE READ). ·        未提交读(READUNCOMMITTED).另一个事务修改了数据,但尚未提交,而本事务中的SELECT会读到这些未被提交的数据(脏读). ·        提交读(READCOMMITTED).本事务读取到的

数据库事务隔离级别、脏读、重复读、不可重复读、幻读

数据库事务四种隔离级别 1.Read Uncommitted(读未提交) :事务中的修改,即使没有提交,其他事务也可以看得到,会导致“脏读”.“幻读”和“不可重复读取”. 2.READ COMMITTED (读提交):大多数主流数据库的默认事务等级,保证了一个事务不会读到另一个并行事务已修改但未提交的数据,避免了“脏读取”,但不能避免“幻读”和“不可重复读取”.该级别适用于大多数系统. 3.REPEATABLE READ(可重复读) :保证了一个事务不会修改已经由另一个事务读取但未提交(回滚)的

SQL Server 中的事务与事务隔离级别以及如何理解脏读, 未提交读,不可重复读和幻读产生的过程和原因

原本打算写有关 SSIS Package 中的事务控制过程的,但是发现很多基本的概念还是需要有 SQL Server 事务和事务的隔离级别做基础铺垫.所以花了点时间,把 SQL Server 数据库中的事务概念,ACID 原则,事务中常见的问题,问题造成的原因和事务隔离级别等这些方面的知识好好的整理了一下. 其实有关 SQL Server 中的事务,说实话因为内容太多, 话题太广,稍微力度控制不好就超过了我目前知识能力范围,就不是三言两语能够讲清楚的.所以希望大家能够指出其中总结的不足之处,对我

不可重复读和幻读的区别

[不可重复读和幻读的区别] 在可重复读中,该sql第一次读取到数据后,就将这些数据加锁,其它事务无法修改这些数据,就可以实现可重复 读了.但这种方法却无法锁住insert的数据,所以当事务A先前读取了数据,或者修改了全部数据,事务B还是可以insert数据提交,这时事务A就会 发现莫名其妙多了一条之前没有的数据,这就是幻读,不能通过行锁来避免.需要Serializable隔离级别 ,读用读锁,写用写锁,读锁和写锁互斥,这么做可以有效的避免幻读.不可重复读.脏读等问题,但会极大的降低数据库的并发能

数据库事务隔离级别-- 脏读、幻读、不可重复读

一.数据库事务隔离级别 数据库事务的隔离级别有4个,由低到高依次为Read uncommitted .Read committed .Repeatable read .Serializable ,这四个级别可以逐个解决脏读 .不可重复读 .幻读 这几类问题. √: 可能出现    ×: 不会出现   脏读 不可重复读 幻读 Read uncommitted √ √ √ Read committed × √ √ Repeatable read × × √ Serializable × × × 注意

可重复读隔离级别里的可能死锁

在今天的文章里我想谈论下在可重复读隔离级别(Transaction Isolation Level Repeatable Read)里,当你运行事务时可能引起的2类死锁.当你使用可重复读(Repeatable Read)隔离级别设置你的事务,SQL Server对读取数据把持需要的共享锁(Shared Locks)直到事务的结束(COMMIT或ROLLBAK).然后当你尝试修改读取的数据(通过UPDATE语句),如果同样的事务多次并发运行,它会英气不同类型的死锁. 循环死锁(Cycle Dead

数据库赃读、不可重复读、幻读

这些是事务并发产生的问题. 事务隔离五种级别:        TRANSACTION_NONE  不使用事务.        TRANSACTION_READ_UNCOMMITTED  允许脏读.        TRANSACTION_READ_COMMITTED  防止脏读,最常用的隔离级别,并且是大多数数据库的默认隔离级别        TRANSACTION_REPEATABLE_READ  可以防止脏读和不可重复读,        TRANSACTION_SERIALIZABLE  可以

MySQL选用可重复读之前一定要想到的事情

原文地址:http://blog.itpub.net/29254281/viewspace-1398273/ MySQL选用可重复读隔离级别之前一定要想到的事情.间隙锁 MySQL在使用之前有三个务必要知道..(随着了解的深入这个极有可能再增加..)1.DDL会引起metadata lock,导致请求连环阻塞,甚至是查询请求.http://blog.itpub.net/29254281/viewspace-1383193/ 2.MySQLDump和XtraBackup的flush table命令