HOLDLOCK is not equivalent to REPEATABLE READ

简介:

HOLDLOCK is not equivalent to REPEATABLE READ and HOLDLOCK does get range locks.  And the semantics of HOLDLOCK and SERIALIZABLE are the same - that is, they both mean that if you rerun the SELECT later in the same transaction you will get back the same result.

The difference between REPEATABLE READ and SERIALIZABLE is REPEATABLE READ guarantees that if you rerun the same select command later in the same transaction, you will get back all the rows that were returned the first time the select ran and these rows will be unchanged.  However, with REPEATABLE READ, you may get back additional, new rows that were not returned the first time.  Therefore REPEATABLE READ does not need range locks, it just puts a shared lock on every returned row.  But SERIALIZABLE not only guarantees that you will get back all the rows that were returned the first time the select ran and these rows will be unchanged, it also guarantees that no new rows will be returned.  In order to do this, it sometimes needs to do a range lock. 

So, for example, to see what REPEATABLE READ does, run

CREATE TABLE TestTable(
	[name] [nvarchar](100) NOT NULL PRIMARY KEY,);


INSERT INTO TestTable (name)
VALUES ('H');
	
INSERT INTO TestTable (name)
VALUES ('K');

INSERT INTO TestTable (name)
VALUES ('P');

SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN TRAN
SELECT* FROM TestTable
		WHERE name Between 'J' And 'L';

Now, if you run sp_lock, you will see a KEY Shared lock on the index.  That's because only the row that is returned (name = 'K') is locked.  And if you open another connection and in the new connection run

INSERT INTO TestTable (name)
values('B');
SELECT * FROM TestTable;

INSERT INTO TestTable (name)
values('T');
SELECT * FROM TestTable;

INSERT INTO TestTable (name)
values('M');
SELECT * FROM TestTable;

Those inserts will work fine.  And the table will now have six rows.  And if you go back to the original connection, and redo the

SELECT * FROM TestTable WHERE name Between 'J' And 'N';

you will now get two rows, Name = 'K' and Name = 'M'.  This is allowed under REPEATABLE READ because the only thing REPEATABLE READ guarantees is that the rows originally returned (Name = 'K' in this case) will be returned unchanged.

But SERIALIZABLE requires that no new rows are returned.  So let's see what happens with SERIALIZABLE.  First commit or rollback all open transactions, and drop the table.  Then run

CREATE TABLE TestTable(
	[name] [nvarchar](100) NOT NULL PRIMARY KEY,);


INSERT INTO TestTable (name)
VALUES ('H');
	
INSERT INTO TestTable (name)
VALUES ('K');

INSERT INTO TestTable (name)
VALUES ('P');

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRAN
SELECT* FROM TestTable
		WHERE name Between 'J' And 'N';

Now if you run sp_lock, you will see that there are range locks.  So, let's go to another connection and try to add new rows.  If you do

INSERT INTO TestTable (name)
values('B');
SELECT * FROM TestTable;

INSERT INTO TestTable (name)
values('T');
SELECT * FROM TestTable;

Then will both work fine.  That's because they will be outside the range locks.  But if you do

INSERT INTO TestTable (name)
values('M');
SELECT * FROM TestTable;

The insert will be blocked.  That's because inserting that row would change the result of the select query in the original connection which is not allowed with SERIALIZABLE.

OK, so now we've seen the difference between REPEATABLE READ and SERIALIZABLE, so the question is does HOLDLOCK do what REPEATABLE READ does or what SERIALIZABLE does.  To find out, commit or rollback all the open transactions, drop the TestTable table, and run

CREATE TABLE TestTable(
	[name] [nvarchar](100) NOT NULL PRIMARY KEY,);


INSERT INTO TestTable (name)
VALUES ('H');
	
INSERT INTO TestTable (name)
VALUES ('K');

INSERT INTO TestTable (name)
VALUES ('P');

SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN TRAN
SELECT* FROM TestTable WITH (HOLDLOCK)
		WHERE name Between 'J' And 'N'

Now run sp_lock.  You will see that you once again have range locks.  And when another connection tries to insert rows, it would be allowed to insert Name='B' and Name = 'T', but inserting Name='M' will be blocked.

So, HOLDLOCK is equivalent to SERIALIZABLE, not REPEATABLE READ.


转载自:http://answers.flyppdevportal.com/MVC/Post/Thread/da738596-1430-4c9e-a1e3-eadaa273f6f8?category=transactsql
















本文转自UltraSQL51CTO博客,原文链接:http://blog.51cto.com/ultrasql/1839580 ,如需转载请自行联系原作者





相关文章
|
20天前
|
数据库
脏读、幻读、不可重复读的定义?
脏读、不可重复读和幻读是数据库事务处理中的三种异常现象。脏读指读取未提交的修改数据;不可重复读指同一事务中多次读取数据不一致;幻读指读取记录范围时,前后读取结果数量不一致。这些现象通常由并发事务操作引起。
36 7
|
关系型数据库 MySQL 数据库
lock_read
lock_read
48 2
|
存储 缓存
【什么是Read Write Through机制】
【什么是Read Write Through机制】
174 0
|
SQL Oracle 关系型数据库
MySQL Repeatable-Read 的一些误解
##### 背景 首先1992 年发表的SQL Standard 对隔离级别进行的定义是根据几个异象(Dirty Read, Non-Repeatable Read, Phantom Read) , 当然这个定义非常模糊, 后面Jim Grey 也有文章说这个不合理, 然而此时MVCC, snapshot isolation 还没被发明. 等有snapshot isolation 以后发
265 0
|
关系型数据库
### avoid read-on-write
### avoid read-on-write 什么是 "read-on-write" problem? 在我们使用最常见的buffer write 中 "read-on-write" 问题指的是当我需要进行小于4k 大小buffer write 的时候, 需要先将数据所在的page 从disk 中读取出放入到page cache, 在page cache 中修改好, 然后再将
1495 0
|
分布式计算 Hadoop