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 ,如需转载请自行联系原作者





相关文章
|
3月前
|
关系型数据库 MySQL 数据库
为什么需要MVCC 隔离级别
【8月更文挑战第5天】
48 7
|
6月前
|
存储 缓存 关系型数据库
RR有幻读问题吗?MVCC能否解决幻读?
RR有幻读问题吗?MVCC能否解决幻读?
62 0
RR有幻读问题吗?MVCC能否解决幻读?
|
关系型数据库 MySQL 数据库
lock_read
lock_read
45 2
|
6月前
|
供应链 数据库
|
存储 缓存
【什么是Read Write Through机制】
【什么是Read Write Through机制】
151 0
|
SQL 存储 Oracle
事务的隔离级别与MVCC
提到数据库,你多半会联想到事务,进而还可能想起曾经背得滚瓜乱熟的ACID,不知道你有没有想过这个问题,事务有原子性、隔离性、一致性和持久性四大特性,为什么偏偏给隔离性设置了级别? 一切还得从事务说起。
|
SQL 缓存 Oracle
一致性读(Read Consistency)的深入解析
一致性读在Oracle中是一个非常重要的概念, 大家一起跟着我先来做下面的一个实验:
一致性读(Read Consistency)的深入解析
|
SQL Oracle 关系型数据库
MySQL Repeatable-Read 的一些误解
##### 背景 首先1992 年发表的SQL Standard 对隔离级别进行的定义是根据几个异象(Dirty Read, Non-Repeatable Read, Phantom Read) , 当然这个定义非常模糊, 后面Jim Grey 也有文章说这个不合理, 然而此时MVCC, snapshot isolation 还没被发明. 等有snapshot isolation 以后发
256 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 中修改好, 然后再将
1488 0