Locking is a mechanism used by the Microsoft SQL Server Database Engine to synchronize access by multiple users to the same piece of data at the same time.
Before a transaction acquires a dependency on the current state of a piece of data, such as by reading or modifying the data, it must protect itself from the effects of another transaction modifying the same data. The transaction does this by requesting a lock on the piece of data. Locks have different modes, such as shared or exclusive. The lock mode defines the level of dependency the transaction has on the data. No transaction can be granted a lock that would conflict with the mode of a lock already granted on that data to another transaction. If a transaction requests a lock mode that conflicts with a lock that has already been granted on the same data, the instance of the Database Engine will pause the requesting transaction until the first lock is released.
When a transaction modifies a piece of data, it holds the lock protecting the modification until the end of the transaction. How long a transaction holds the locks acquired to protect read operations depends on the transaction isolation level setting. All locks held by a transaction are released when the transaction completes (either commits or rolls back).
Applications do not typically request locks directly. Locks are managed internally by a part of the Database Engine called the lock manager. When an instance of the Database Engine processes a Transact-SQL statement, the Database Engine query processor determines which resources are to be accessed. The query processor determines what types of locks are required to protect each resource based on the type of access and the transaction isolation level setting. The query processor then requests the appropriate locks from the lock manager. The lock manager grants the locks if there are no conflicting locks held by other transactions.
TRANSACTION ISOLATION LEVEL
Controls the locking and row versioning behavior of Transact-SQL statements issued by a connection to SQL Server. There are 5 levels:
Statements can read rows that have been modified by other transactions but not yet committed.
Specifies that statements cannot read data that has been modified but not committed by other transactions. This prevents dirty reads. Data can be changed by other transactions between individual statements within the current transaction, resulting in nonrepeatable reads or phantom data. This option is the SQL Server default.
Specifies that statements cannot read data that has been modified but not yet committed by other transactions and that no other transactions can modify data that has been read by the current transaction until the current transaction completes.
Specifies that data read by any statement in a transaction will be the transactionally consistent version of the data that existed at the start of the transaction.
Statements cannot read data that has been modified but not yet committed by other transactions. No other transactions can modify data that has been read by the current transaction until the current transaction completes. Other transactions cannot insert new rows with key values that would fall in the range of keys read by any statements in the current transaction until the current transaction completes.
More details on all the transaction isolation modes can be found here in Books Online
Lock Granularity and Hierarchies
Locking at a smaller granularity, such as rows, increases concurrency but has a higher overhead because more locks must be held if many rows are locked. Locking at a larger granularity, such as tables, are expensive in terms of concurrency because locking an entire table restricts access to any part of the table by other transactions. However, it has a lower overhead because fewer locks are being maintained.
The following table shows the resources that the Database Engine can lock.
A row identifier used to lock a single row within a heap.
A row lock within an index used to protect key ranges in serializable transactions.
An 8-kilobyte (KB) page in a database, such as data or index pages.
A contiguous group of eight pages, such as data or index pages.
A heap or B-tree. A lock protecting an index or the heap of data pages in a table that does not have a clustered index.
The entire table, including all data and indexes.
A database file.
An application-specified resource.
An allocation unit.
The entire database.
The Microsoft SQL Server Database Engine locks resources using different lock modes that determine how the resources can be accessed by concurrent transactions.
The following table shows the resource lock modes that the Database Engine uses.
Used for read operations that do not change or update data, such as a SELECT statement.
Used on resources that can be updated. Prevents a common form of deadlock that occurs when multiple sessions are reading, locking, and potentially updating resources later.
Used for data-modification operations, such as INSERT, UPDATE, or DELETE. Ensures that multiple updates cannot be made to the same resource at the same time.
Used to establish a lock hierarchy. The types of intent locks are: intent shared (IS), intent exclusive (IX), and shared with intent exclusive (SIX).
Used when an operation dependent on the schema of a table is executing. The types of schema locks are: schema modification (Sch-M) and schema stability (Sch-S).
Bulk Update (BU)
Used when bulk copying data into a table and the TABLOCK hint is specified.
Protects the range of rows read by a query when using the serializable transaction isolation level. Ensures that other transactions cannot insert rows that would qualify for the queries of the serializable transaction if the queries were run again.
Full details of SQL Server’s lock modes can be found in this article.
Lock Compatibility (Database Engine)
Multiple transactions can acquire concurrent locks on the same resource if their lock modes are compatible. If a transaction requests a lock mode that conflicts with an existing lock, the transaction is paused until the first lock is freed. For a full list of lock mode compatibilty see this article
Key-range locks protect a range of rows implicitly included in a record set being read by a Transact-SQL statement while using the serializable transaction isolation level. The serializable isolation level requires that any query executed during a transaction must obtain the same set of rows every time it is executed during the transaction. A key range lock protects this requirement by preventing other transactions from inserting new rows whose keys would fall in the range of keys read by the serializable transaction.
Key-range locking prevents phantom reads. By protecting the ranges of keys between rows, it also prevents phantom insertions into a set of records accessed by a transaction.
A key-range lock is placed on an index, specifying a beginning and ending key value. This lock blocks any attempt to insert, update, or delete any row with a key value that falls in the range because those operations would first have to acquire a lock on the index. For example, a serializable transaction could issue a SELECT statement that reads all rows whose key values are between ‘AAA‘ and ‘CZZ‘. A key-range lock on the key values in the range from ‘AAA‘ to ‘CZZ‘ prevents other transactions from inserting rows with key values anywhere in that range, such as ‘ADG‘, ‘BBD‘, or ‘CAL‘.
Lock escalation is the process of converting many fine-grain locks into fewer coarse-grain locks, reducing system overhead while increasing the probability of concurrency contention. See the Lock Escalation (Database Engine) chapter on BOL for full details
The Microsoft SQL Server Database Engine uses a dynamic locking strategy to determine the most cost-effective locks. The Database Engine automatically determines what locks are most appropriate when the query is executed, based on the characteristics of the schema and query. For example, to reduce the overhead of locking, the optimizer may choose page-level locks in an index when performing an index scan.
Dynamic locking has the following advantages:
- Simplified database administration. Database administrators do not have to adjust lock escalation thresholds
- Increased performance. The Database Engine minimizes system overhead by using locks appropriate to the task
- Application developers can concentrate on development. The Database Engine adjusts locking automatically
Displaying Locking Information (Database Engine)
Microsoft SQL Server 2005 provides several ways to get information about the current locking activity in an instance of the Database Engine.
A deadlock occurs when two or more tasks permanently block each other by each task having a lock on a resource which the other tasks are trying to lock. Both transactions in a deadlock will wait forever unless the deadlock is broken by an external process. More information on deadlocks and techniques to avoid them can be found in BOL.