Вход на сайт

Просмотр новости

Найдите то, что Вас интересует

And Then There Was The Time RCSI Actually Made Query Results More Accurate.

Дата публикации: 10-06-2026 13:15:12

Normally when I tell people about SQL Server’s optimistic concurrency isolation levels, Read Committed Snapshot Isolation (RCSI) and Snapshot Isolation (SI), I have to give them a little speech about how they need to test their queries because the results can change. Recently, though, I was working with a client who was getting the wrong query […]

Основное содержимое страницы с новостью.

Normally when I tell people about SQL Server’s optimistic concurrency isolation levels, Read Committed Snapshot Isolation (RCSI) and Snapshot Isolation (SI), I have to give them a little speech about how they need to test their queries because the results can change.

Recently, though, I was working with a client who was getting the wrong query results under the default pessimistic isolation level – and we switched to RCSI in order to fix it! I’m not going to explain RCSI or SI here – use the link above for a tutorial on the basics – but rather I’m going to focus on a demo script I wrote up to show the issue they were having, and how RCSI fixed it.

Let’s say we need to track driver rankings in a race to know who’s in first, who’s in second, and so forth. We’ve built a leaderboard table with a row for each driver, showing their position. I’m going to use

Transact-SQL

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

DROP DATABASE IF EXISTS [IsolationLevelDemo];

GO

CREATE DATABASE [IsolationLevelDemo]

GO

ALTER DATABASE [IsolationLevelDemo] SET READ_COMMITTED_SNAPSHOT OFF WITH NO_WAIT

GO

USE [IsolationLevelDemo];

GO

DROP TABLE IF EXISTS dbo.F1Standings;

GO

CREATE TABLE dbo.F1Standings

(

    Id INT IDENTITY(1,1) PRIMARY KEY CLUSTERED,

    DriverName varchar(50) NOT NULL,

    Position int NOT NULL

);

INSERT dbo.F1Standings (DriverName, Position)

VALUES

('Kimi Antonelli', 1),

('George Russell', 2),

('Charles Leclerc', 3),

('Lewis Hamilton', 4);

GO

To check driver standings for our leaderboard, we run this query:

Transact-SQL

1

2

3

SELECT DriverName, Position

FROM [IsolationLevelDemo].dbo.F1Standings

ORDER BY Position;

And the results make sense:

Results when nothing is happening

Whenever one driver passes another, we need to make two changes: we need to move the faster driver UP one position, and the slower driver DOWN one position. We don’t have a concept of ties in our race. So our update transaction looks like this:

Transact-SQL

1

2

3

4

5

6

7

8

9

10

11

BEGIN TRAN;

UPDATE dbo.F1Standings

SET Position = 3

WHERE DriverName = 'Lewis Hamilton';

UPDATE dbo.F1Standings

SET Position = 4

WHERE DriverName = 'Charles Leclerc';

COMMIT;

(Yes, you could do this with plus-one-minus-one, and with parameter-driven driver names, but I’m keeping this simple.)

Our users report that sometimes during a race, they see something they shouldn’t: a tie.

There aren't supposed to be ties here

The business users say, “Wait a second: we’re doing everything in a transaction. It’s all gotta roll forward together, or roll backwards together, right? We’re not using NOLOCK, so how are we seeing inaccurate results?!?”

To see the problem:

  1. Start the transaction and run the first update statement, setting Lewis to 3rd place.
  2. In ANOTHER window/session, run the leaderboard select. It will appear to be blocked.
  3. Go back to the transaction window/session and run the second update statement and the commit, setting Charles to 4th place.
  4. The leaderboard select window will show both Charles and Lewis in 3rd place.
Here’s why SQL Server is showing the “wrong” results by default.

When our table is first populated, our clustered index looks like this, sorted by Id:

Clustered index order

After our first update query runs, the table changes to this:

Lewis in 3rd

Here’s the tricky part: row Id #4 is now locked, because we’ve updated Lewis to 3rd place. It doesn’t move where his row is in the table – his row is still last – but right now, it’s locked.

Next, if the select runs in our default pessimistic (Read Committed) isolation level:

Transact-SQL

1

2

3

SELECT DriverName, Position

FROM [IsolationLevelDemo].dbo.F1Standings

ORDER BY Position;

SQL Server starts reading from the beginning of the table. (We don’t have an index – that makes the demo even more complicated, and I wanna keep this simple for starters – you can explore how indexes and different key orders affect this later on your own if you’d like. I can lead you to water but I can’t make you a margarita. Go play with indexes, indexed views, columnstore, In-Memory OLTP, and MongoDB on your own time. I’m trying to get this blog post done in under 1,000 words, in an airport terminal. Yes, the client had indexes, and that made the situation even more fun to diagnose. No, I’m not being sarcastic about “fun”, and yes, I have a twisted definition of fun. Clients get nervous when I jump and clap for joy at their problems.)

It reads from the beginning of the table, and reads rows 1, 2, and 3 because those rows aren’t locked yet. That means its reads INCLUDE the currently UNLOCKED Charles, and the select sees him in third place. However, the query now pauses at row 4, unable to read it yet, blocked, because we’ve updated row 4.

The second query in our update transaction now needs a lock on row 3, Charles, and it can get it. The select doesn’t hold out a lock on that row – it’s already done reading it, and the select has already seen Charles at 3rd place. The update locks Id 3 (Charles), sets his place to 4th, commits the transaction, and releases its lock on all rows – allowing our select to finally read row 4, Lewis – but he’s now in 3rd place!

To fix that, we turned on RCSI.

Read Committed Snapshot Isolation turns on a version store that allows read queries to see a previous version of locked rows, before changes are made. This changes what happens when we step through the process:

  1. Start the transaction and run the first update statement, setting Lewis to 3rd place.
  2. In ANOTHER window/session, run the leaderboard select. It will not be blocked this time, and will be able to read the previous version of Lewis’s row, showing him in 4th place. The leaderboard query finishes, showing a result that is technically true because the update hasn’t committed yet.

Accurate leaderboard

You could argue, “Brent, that’s not the right leaderboard, because Lewis just passed Charles!” I hear you, but in a fast-and-furious situation like this, I’d rather show a set of leaderboard results that were technically true at some moment in time, rather than showing an impossible set of results that were never true in any moment of time.

If you were really gonna do it right, you’d lock both rows in a single update statement:

Transact-SQL

1

2

3

4

5

6

UPDATE dbo.F1Standings

SET Position = CASE

    WHEN DriverName = 'Lewis Hamilton' THEN 3

    WHEN DriverName = 'Charles Leclerc' THEN 4

    END

WHERE DriverName IN ('Charles LeClerc', 'Lewis Hamilton');

That way you lock both rows in a single statement, no separate transaction is required, and read queries will see the right results regardless of what isolation level we use. (Unless you’re using NOLOCK, aka YOLO results mode, but that’s for another post.)

Схожие новости

#Наименование новостиТональностьИнформативностьДата публикации
1“But NOLOCK won’t hurt me if I’m using an index!”013.9515-09-2026
2openSUSE Leap 16.1 RC Released With New Immutable Mode011.8128-09-2026
3Cve-2026-59873030.1329-07-2026
4 Nvidia's DLSS 5 isn't what you think it is, and I tested it at Gamescom 011.5201-09-2026
5Подключение сайта к РСЯ после его покупки03.4921-09-2026
6LSI-Stresstest bestätigt deutschen Instituten hohe Resilienz018.0324-09-2026
7Beyond Static Coexistence: Architecting Non-Stationary RFFE Systems for Multi-State Devices03012-08-2026
8How Certinia’s System of Action smoothens service experiences08.425-09-2026
9How Equity Changes01011-09-2026

Классификация: . Схожих патентов: 0. Схожих новостей: 9. Тональность: 0. Информативность: 7.05. Источник: www.brentozar.com.