|
| 1 | +<!-- Always leave the MS logo --> |
| 2 | + |
| 3 | + |
| 4 | +# Optimized Locking: Transaction ID (TID) locking internals |
| 5 | + |
| 6 | +This sample describes how to read and interpret the Transaction ID (TID) stored in row data pages. |
| 7 | + |
| 8 | +## Background |
| 9 | + |
| 10 | +Optimized Locking is a 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 (TID)? |
| 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 tags that row with its own TID, so each row in the database is labeled with the last TID that modified it. |
| 29 | + |
| 30 | +### Contents |
| 31 | + |
| 32 | +[About this sample](#about-this-sample)<br/> |
| 33 | +[Before you begin](#before-you-begin)<br/> |
| 34 | +[Run this sample](#run-this-sample)<br/> |
| 35 | +[Sample Details](#sample-details)<br/> |
| 36 | +[Disclaimers](#disclaimers)<br/> |
| 37 | +[Related links](#related-links)<br/> |
| 38 | + |
| 39 | +<a name=about-this-sample></a> |
| 40 | +## About this sample |
| 41 | + |
| 42 | +- **Applies to:** SQL Server 2025 (or higher), Azure SQL Database |
| 43 | +- **Key features:** Optimized Locking |
| 44 | +- **Workload:** No workload related to this sample |
| 45 | +- **Programming Language:** T-SQL |
| 46 | +- **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) | [X](https://twitter.com/segovoni) |
| 47 | + |
| 48 | +<a name=before-you-begin></a> |
| 49 | +## Before you begin |
| 50 | + |
| 51 | +To run this sample, you need the following prerequisites. |
| 52 | + |
| 53 | +**Software prerequisites:** |
| 54 | + |
| 55 | +1. SQL Server 2025 (or higher) or Azure SQL Database |
| 56 | + |
| 57 | +<a name=run-this-sample></a> |
| 58 | +## Run this sample |
| 59 | + |
| 60 | +### Setup code |
| 61 | + |
| 62 | +1. Download [create-configure-optimizedlocking-db.sql](sql-scripts/create-configure-optimizedlocking-db.sql) T-SQL script from sql-scripts folder |
| 63 | +2. Verify that a database named OptimizedLocking does not already exist in your SQL Server instance |
| 64 | +3. Execute create-configure-optimizedlocking-db.sql script on your SQL Server instance |
| 65 | +4. Run the commands described in the sample details section |
| 66 | + |
| 67 | +<a name=sample-details></a> |
| 68 | +## Sample Details |
| 69 | + |
| 70 | +Let's consider the table dbo.TelemetryPacket, with the schema defined in the following T-SQL code snippet. |
| 71 | + |
| 72 | +```sql |
| 73 | +USE [OptimizedLocking] |
| 74 | +GO |
| 75 | + |
| 76 | +CREATE TABLE dbo.TelemetryPacket |
| 77 | +( |
| 78 | + PacketID INT IDENTITY(1, 1) |
| 79 | + ,Device CHAR(8000) DEFAULT ('Something') |
| 80 | +); |
| 81 | +GO |
| 82 | +``` |
| 83 | + |
| 84 | +The table schema is designed so that each row occupies exactly one data page. |
| 85 | + |
| 86 | +Insert three rows with default values into the dbo.TelemetryPacket table. Note that this is done in a single transaction. |
| 87 | + |
| 88 | +Before committing the transaction, we query the [sys.dm_tran_locks](https://learn.microsoft.com/sql/relational-databases/system-dynamic-management-views/sys-dm-tran-locks-transact-sql) DMV, which exposes the TID locks as a new resource type = `XACT`. |
| 89 | + |
| 90 | +```sql |
| 91 | +BEGIN TRANSACTION; |
| 92 | + |
| 93 | +INSERT INTO dbo.TelemetryPacket DEFAULT VALUES; |
| 94 | +INSERT INTO dbo.TelemetryPacket DEFAULT VALUES; |
| 95 | +INSERT INTO dbo.TelemetryPacket DEFAULT VALUES; |
| 96 | + |
| 97 | +SELECT |
| 98 | + l.resource_description |
| 99 | + ,l.resource_associated_entity_id |
| 100 | + ,l.resource_lock_partition |
| 101 | + ,l.request_mode |
| 102 | + ,l.request_type |
| 103 | + ,l.request_status |
| 104 | + ,l.request_owner_type |
| 105 | +FROM |
| 106 | + sys.dm_tran_locks AS l |
| 107 | +WHERE |
| 108 | + (l.request_session_id = @@SPID) |
| 109 | + AND (l.resource_type = 'XACT'); |
| 110 | + |
| 111 | +COMMIT; |
| 112 | +``` |
| 113 | + |
| 114 | +The resource_description column, in this example, reports the `XACT` value equal to `10:1147:0`. |
| 115 | + |
| 116 | +TID `1147` represents the identifier of the transaction that inserted the rows and it will be stored in the row data page if the transaction is confirmed. Every subsequent change to the rows will update the TID. |
| 117 | + |
| 118 | +Now let's make a change on the row identified by the PacketID value 2 and before confirming the transaction let's repeat again the query on the DMV. |
| 119 | + |
| 120 | +```sql |
| 121 | +BEGIN TRANSACTION; |
| 122 | + |
| 123 | +UPDATE |
| 124 | + t |
| 125 | +SET |
| 126 | + t.Device = 'Something updated' |
| 127 | +FROM |
| 128 | + dbo.TelemetryPacket AS t |
| 129 | +WHERE |
| 130 | + t.PacketID = 2; |
| 131 | + |
| 132 | +SELECT |
| 133 | + l.resource_description |
| 134 | + ,l.resource_associated_entity_id |
| 135 | + ,l.resource_lock_partition |
| 136 | + ,l.request_mode |
| 137 | + ,l.request_type |
| 138 | + ,l.request_status |
| 139 | + ,l.request_owner_type |
| 140 | +FROM |
| 141 | + sys.dm_tran_locks AS l |
| 142 | +WHERE |
| 143 | + (l.request_session_id = @@SPID) |
| 144 | + AND (l.resource_type = 'XACT'); |
| 145 | + |
| 146 | +COMMIT; |
| 147 | +``` |
| 148 | + |
| 149 | +Even for the `UPDATE` command, the resource_description column displays the TID of the transaction that is modifying the row. If the transaction is confirmed, the TID will be stored in the data page of the row itself. |
| 150 | + |
| 151 | +<a name=disclaimers></a> |
| 152 | +## Disclaimers |
| 153 | + |
| 154 | +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. |
| 155 | + |
| 156 | +<a name=related-links></a> |
| 157 | +## Related Links |
| 158 | + |
| 159 | +- [Optimized locking](https://learn.microsoft.com/sql/relational-databases/performance/optimized-locking) |
0 commit comments