Lock Modes
Data in a database is not like
a book, which can only be in the possession of one person at a time. If
you are reading a book, the book is in your hands and other people
can’t read it. Data is more like a notice on a board. You and other
people can read it at the same time. However, if you want to change it,
then you need to take the notice down off the board, and no one else
can change it at the same time. Whether or not they can read it while
it is being changed is a separate matter (the isolation level), but
this scenario is related to the concept of lock modes, and the compatibility matrix between them, as described in the following sections.
Shared Lock Mode (S)
When a read request for a row
of data is made by a task, by default, SQL Server will request a lock
in shared mode. Shared mode is compatible with most other locks, as it
is only permitted to read the row on the data page.
Update Lock Mode (U)
Update mode is a special kind of lock.
It is used when searching data during a data modification request. The
process is straightforward: SQL Server uses the update lock by locating
the data and then preventing others from updating it. It prevents other
requests from modifying the data by virtue of the update lock’s
compatibility with other locks. Any other requests wishing to lock the
resource with an update or exclusive lock are forced to wait. However,
in order to effect the data modification, the update lock must be
converted to an exclusive lock. As the update lock has blocked all
other data modification locks, all it needs to do is wait until it can
get an exclusive lock when the last, if any, shared locks have been
released. This allows for greater concurrency in the system as opposed
to all writers just taking exclusive locks. If the latter were the
case, then blocking would be a much greater problem. Concurrent queries
would be blocked for the entire duration of the update (the read part
and the write) as opposed to just the write.
Exclusive Lock Mode (X)
Exclusive locks are used for data modification via INSERT, UPDATE, and DELETE
statements. In terms of compatibility, exclusive locks are not
compatible with any other kind of lock, including other exclusive
locks. All locks must wait for the exclusive lock to be released before
they can proceed; provided your solution isn’t using dirty reads and
therefore bypassing the lock entirely. As mentioned earlier, exclusive
locks are held until the end of the transaction, whether that is by
commit or rollback.
Schema Lock Modes (Sch-S), (Sch-M)
There are actually two types of schema lock mode: schema modification (Sch-M) and schema stability (Sch-S).
These locks are taken by different processes but basically boil down to
the same thing. A query takes a schema-modification lock when it wants
to change the schema in some way. Schema stability is designed to block
schema modification if needed. For example, when a stored procedure is
compiled, a schema-stability lock is taken to ensure that no one
changes the table during the compilation process. Alternatively, a
schema-modification lock is taken when altering a table, as you have
seen, but also when performing partition switching. In this case, a
Sch-M is taken on both the source and the target.
Intent Lock Modes (IS), (IU), (IX)
As shown previously in the discussion
of lock granularity, SQL Server can grant locks at various levels or
degrees of granularity. These levels are used to form a hierarchy
within SQL Server. A row is at the bottom of this hierarchy and belongs
to a page; the page itself belongs to a table, and so on. The lock
hierarchy is covered in detail in the next section, but the purpose of
the intent lock is to indicate at the higher levels of the lock
hierarchy that a part of the resource has a lock held against it. This
allows checks to be performed at the level at which a lock is
requested, which is a great performance optimization.
If an exclusive row lock is acquired on a table,
the page and the table will have intent exclusive locks held against
them. Consequently, if another process wants to take out a table lock,
it can check at the table level, see that there is an intent exclusive
lock present, and know it is blocked without having to scan the entire
table looking for conflicting locks.
Intent locks shouldn’t be considered as locks in
the same vein as a shared, update, or exclusive lock. They act as
indicators to SQL Server, pointing out that an actual lock has been
acquired at a lower level in that hierarchy for that resource.
Consider an ALTER TABLE
statement, which needs to be executed when no other users are trying to
run queries against the table. If the table changed during the query,
this would be very bad news indeed. However, it would also be a massive
pain to check the locks for every row of the table to determine whether
any are being read or modified. Instead, a table-level check takes
place, which indicates immediately in a single request whether any
other activity is occurring in the table.
Try this for yourself. In Session 1, run the following code (code file Ch6IntentLockModes.sql):
USE AdventureWorks2012;
/* SESSION 1 */
BEGIN TRANSACTION;
UPDATE Production.Product
SET SafetyStockLevel = SafetyStockLevel
WHERE ProductID =1;
--ROLLBACK TRAN;
SELECT resource_type
,resource_subtype
,resource_description
,resource_associated_entity_id
,request_mode
,request_status
FROM sys.dm_tran_locks
WHERE request_session_id= @@spid;
Note the intent locks (request_mode is IX) on page and object in Figure 1. Now try to run this ALTER TABLE statement in another query window:
USE AdventureWorks2012;
/* SESSION 2 */
BEGIN TRANSACTION;
ALTER TABLE Production.Product
ADD TESTCOLUMN INT NULL;
--ROLLBACK TRANSACTION;
The ALTER TABLE
statement should be blocked. How do you know this? First, it will take
forever to make that change, as the explicit transaction in Session 1
hasn’t been closed. However, more important, look at row 5 in the
output shown in Figure 2 (the query for sys.dm_tran_locks has been rerun in the Session 1 window but also includes the SPID used for Session 2). Note that the request_mode contains a schema modify lock, and that the request_status is set to WAIT. This means it is on the wait list, which ties back to the fact that it is blocked. Finally, look at the resource_type. It’s an object resource request. The database engine checks for the existence of an object resource_type for the same resource_associated_entity_id as the one requested. Because one exists, the ALTER TABLE cannot proceed.
You might want to roll back those transactions now to release the locks.