|
| 1 | +<!-- Always leave the MS logo --> |
| 2 | + |
| 3 | + |
| 4 | +# SQL Server 2025 Optimized Locking: Transaction ID (TID) Locking internals |
| 5 | + |
| 6 | +This sample describes how to read and interpret the Transaction ID stored in row data pages. |
| 7 | + |
| 8 | +## Background |
| 9 | + |
| 10 | +Optimized Locking is a SQL Server 2025 database engine feature designed to reduce the memory used for lock management, decrease the phenomenon known as lock escalation, and increase workload concurrency. |
| 11 | + |
| 12 | +Optimized Locking depends on two technologies that have long been part of the SQL Server engine: |
| 13 | +- [Accelerated Database Recovery (ADR)](https://learn.microsoft.com/sql/relational-databases/accelerated-database-recovery-concepts) is a required prerequisite for enabling Optimized Locking |
| 14 | +- [Read Committed Snapshot Isolation (RCSI)](https://learn.microsoft.com/sql/t-sql/statements/set-transaction-isolation-level-transact-sql) is not a strict requirement, but allows full benefit from Optimized Locking |
| 15 | + |
| 16 | +Optimized Locking is based on two key mechanisms: |
| 17 | +- Transaction ID (TID) locking |
| 18 | +- Lock After Qualification (LAQ) |
| 19 | + |
| 20 | +### What is the Transaction ID? |
| 21 | + |
| 22 | +The Transaction ID (TID) is a unique transaction identifier. |
| 23 | + |
| 24 | +When a [row-versioning based isolation level](https://learn.microsoft.com/en-us/sql/relational-databases/sql-server-transaction-locking-and-row-versioning-guide#Row_versioning) is active, or when [Accelerated Database Recovery (ADR)](https://learn.microsoft.com/sql/relational-databases/accelerated-database-recovery-concepts) is enabled, every row in the database internally contains a transaction identifier. |
| 25 | + |
| 26 | +The TID is stored on disk in the additional 14 bytes that are associated with each row when features such as RCSI or ADR are enabled. |
| 27 | + |
| 28 | +Every transaction that modifies a row, it tags that row with its own TID, so each row in the database is labeled with the last TID that modified it. |
| 29 | + |
| 30 | + |
| 31 | +### Contents |
| 32 | + |
| 33 | +[About this sample](#about-this-sample)<br/> |
| 34 | +[Before you begin](#before-you-begin)<br/> |
| 35 | +[Run this sample](#run-this-sample)<br/> |
| 36 | +[Sample Details](#sample-details)<br/> |
| 37 | +[Disclaimers](#disclaimers)<br/> |
| 38 | +[Related links](#related-links)<br/> |
| 39 | + |
| 40 | +<a name=about-this-sample></a> |
| 41 | +## About this sample |
| 42 | + |
| 43 | +- **Applies to:** SQL Server 2025 (or higher) |
| 44 | +- **Key features:** SQL Server 2025 Optimized Locking |
| 45 | +- **Workload:** No workload related to this sample |
| 46 | +- **Programming Language:** T-SQL |
| 47 | +- **Authors:** [Sergio Govoni](https://www.linkedin.com/in/sgovoni/) | [Microsoft MVP Profile](https://mvp.microsoft.com/mvp/profile/c7b770c0-3c9a-e411-93f2-9cb65495d3c4) | [Blog](https://segovoni.medium.com/) | [GitHub](https://github.com/segovoni) | [Twitter](https://twitter.com/segovoni) |
| 48 | + |
| 49 | +<a name=before-you-begin></a> |
| 50 | +## Before you begin |
| 51 | + |
| 52 | +To run this sample, you need the following prerequisites. |
| 53 | + |
| 54 | +**Software prerequisites:** |
| 55 | + |
| 56 | +1. SQL Server 2025 (or higher) |
| 57 | + |
| 58 | +<a name=run-this-sample></a> |
| 59 | +## Run this sample |
| 60 | + |
| 61 | +### Setup code |
| 62 | + |
| 63 | +1. Download [create-configure-optimizedlocking-db.sql T-SQL script](sql-scripts) from sql-scripts folder |
| 64 | +2. Check if a database called OptimizedLocking does not exist in your SQL Server 2025 instance |
| 65 | +3. Execute create-configure-optimizedlocking-db.sql script on your SQL Server 2025 instance |
| 66 | +4. Run the commands described in the sample details section |
| 67 | + |
| 68 | +<a name=sample-details></a> |
| 69 | +## Sample Details |
| 70 | + |
| 71 | +Currently, the only way to read the Transaction ID of a row is by using the `DBCC PAGE` command. |
| 72 | + |
| 73 | +Let's consider the table dbo.TelemetryPacket, with the schema defined in the following T-SQL code snippet. |
| 74 | + |
| 75 | +```sql |
| 76 | +USE [OptimizedLocking] |
| 77 | +GO |
| 78 | + |
| 79 | +CREATE TABLE dbo.TelemetryPacket |
| 80 | +( |
| 81 | + PacketID INT IDENTITY(1, 1) |
| 82 | + ,Device CHAR(8000) DEFAULT ('Something') |
| 83 | +); |
| 84 | +GO |
| 85 | +``` |
| 86 | + |
| 87 | +The table schema is designed so that each row occupies exactly one data page. |
| 88 | + |
| 89 | +Insert three rows with default values into the dbo.TelemetryPacket table. Note that this is done in a single transaction. |
| 90 | + |
| 91 | +```sql |
| 92 | +BEGIN TRANSACTION |
| 93 | +INSERT INTO dbo.TelemetryPacket DEFAULT VALUES; |
| 94 | +INSERT INTO dbo.TelemetryPacket DEFAULT VALUES; |
| 95 | +INSERT INTO dbo.TelemetryPacket DEFAULT VALUES; |
| 96 | +COMMIT |
| 97 | +``` |
| 98 | + |
| 99 | +Let's explore the content of the dbo.TelemetryPacket table, enriched with the PageId column, which shows the result of the undocumented function sys.fn_PhysLocFormatter. Use this function to correlate the rows returned by the `SELECT` with their physical location on disk. |
| 100 | + |
| 101 | +```sql |
| 102 | +USE [OptimizedLocking] |
| 103 | +GO |
| 104 | + |
| 105 | +SELECT |
| 106 | + * |
| 107 | + ,PageId = sys.fn_PhysLocFormatter(%%physloc%%) |
| 108 | +FROM |
| 109 | + dbo.TelemetryPacket; |
| 110 | +``` |
| 111 | + |
| 112 | +The values shown in the PageId column represent the physical location of the data. |
| 113 | + |
| 114 | +Let's look at the row where PacketID equals 1. |
| 115 | + |
| 116 | +The value (1:XXXX:0) in the PageId column is composed of three parts separated by ":". Here is what each part represents: |
| 117 | +- 1 is the numeric identifier of the database file (file number) where the page is located |
| 118 | +- XXXX is the page number inside file 1 of the database |
| 119 | +- 0 is the slot number |
| 120 | + |
| 121 | +Use the `DBCC PAGE` command to inspect the TID of page XXXX. |
| 122 | + |
| 123 | +```sql |
| 124 | +DBCC PAGE ('OptimizedLocking', 1, XXXX, 3); |
| 125 | +``` |
| 126 | + |
| 127 | +The value of the unique transaction identifier (TID) that modified the row with PacketID equal to 1 is in the Version Information section, under the Transaction Timestamp attribute. |
| 128 | + |
| 129 | +<a name=disclaimers></a> |
| 130 | +## Disclaimers |
| 131 | + |
| 132 | +The code included in this sample is not intended to be a set of best practices on how to build scalable enterprise grade applications. This is beyond the scope of this sample. |
| 133 | + |
| 134 | +<a name=related-links></a> |
| 135 | +## Related Links |
| 136 | + |
| 137 | +- [Optimized locking](https://learn.microsoft.com/sql/relational-databases/performance/optimized-locking) |
0 commit comments