I have a table with the following schema :
CREATE TABLE [dbo].[Events]
(
[sequenceNumber] [bigint] IDENTITY(1,1) NOT NULL,
[event] [varbinary](max) NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
To debug some locking related issues I was trying to understand the locking mechanisms of SQL Server and trying to replicate the situations locally.
But there is one thing I am unable to test and that is the shared locks that the SELECT queries acquire on the rows being read. To test this, I was running the following T-SQL :
BEGIN TRANSACTION;
SELECT TOP (1)
sequenceNumber, event
FROM
Events
ORDER BY
sequenceNumber DESC;
From my understanding, this should have resulted in a S
lock on the rows or table? But upon checking both sys.dm_tran_locks
and sp_lock
, there are no locks visible.
On the other hand if I try to execute a write and in parallel run the read command, then I am able to observe the X
and S
locks.
-- Write query
BEGIN TRANSACTION;
INSERT INTO Events (event) VALUES (CONVERT(varbinary, 'Two'))
-- Read query run after above write
BEGIN TRANSACTION;
SELECT TOP (1)
sequenceNumber, event
FROM
Events
ORDER BY
sequenceNumber DESC;
Result of sp_lock
in this case :
Why are the shared locks not visible with only the SELECT
statement?
Does the engine perform the reads without any shared locks in these cases?
I think you're running into a timing issue. Reading the docs for the query hint serializable:
Equivalent to HOLDLOCK. Makes shared locks more restrictive by holding them until a transaction is completed, instead of releasing the shared lock as soon as the required table or data page is no longer needed, whether the transaction has been completed or not. The scan is performed with the same semantics as a transaction running at the SERIALIZABLE isolation level.
(emphasis mine) Which is to say that the locks are obtained, the data is read, and the lock is released faster than you can tab over to another session and run sp_lock
.
To further your exploration, I can see (at least) two options
serializable
hint (or isolation level) on your query so that the select doesn't release the locks.Of those, I think the XE route is better as you're not changing concurrency semantics which can change the nature and duration of locks. But the serializable route is certainly easier to set up.