As per my study, I found that a lot of people mention that the covering index would help to reduce the "key lookup" blocking. I was keen to test and understand it, but my testing did not show the expected result.
First I run the code below, I trying to update the table on the first transaction without commit and then on another session, I run a query to select the col2 and col3 from the tableA. According to the book, the second session will be block by the first session.
-- Create the table for testing
CREATE TABLE [TableA]
(
[col1] INT,
[col2] INT,
[col3] INT,
[col4] CHAR(100) DEFAULT('abc') NOT NULL
);
GO
DECLARE @int INT;
SET @int = 1;
-- Load data into the table
WHILE (@int <= 1000)
BEGIN
INSERT INTO [TableA]
([col1], [col2], [col3], [col4])
VALUES (@int*2, @int*2, @int*2, @int*2);
SET @int = @int + 1;
END
GO
CREATE CLUSTERED INDEX [cidx_TableA]
ON [TableA] ([col1]);
-- Create a non-clustered index
CREATE NONCLUSTERED INDEX [idx_TableA_col2]
ON [TableA] ([col2]);
GO
BEGIN TRANSACTION
UPDATE [TableA] SET col3=999 WHERE Col2=4
--Rollback
--ON another session, I query this.
SELECT [col2],col3 FROM [TableA]
WHERE [col2]=66
option (recompile)
The second session did using the key lookup to the Cluster index cidx_TableA and return the result, I expect it would no return any result because suppose it would lock by the update on first session. Why?
I am not yet create the covering index yet, because it did not make any blocking at all. So, did covering index really work?
Best Answer
There's a lot to unpack here, but I will say probably the best decision would be to start looking at Read Committed Snapshot Isolation (RCSI) this means you no longer require locks to read data (and as a bonus, you get meaningful results).
With the indexes as they currently are (assuming access via
idx_TableA_col2
), your update statement is going to lockidx_TableA_col2
index for the rows that get updatedYour select statement does not need to read any of those locked bits because you can seek to
WHERE [col2]=66
in theidx_TableA_col2
index (avoiding the locked index entries) and use that to access just the rows in the table that match this condition (again, these rows are not locked).The locking problem a covering index is trying to avoid is for where a row is updated but the query does not need to worry about this column. Your
update
statement is updatingcol3
, which is involved in your select statement so your covering index on([col2],[col3])
would get locked for these rows too as the update statement will need to update the entries in this index.In order to demo the problem a covering index would solve, you would need to change the
update
statement to not be updatingcol3
but have thewhere
filter return the same rows:But as I mentioned in the first paragraph, using RCSI will avoid this problem completely.