SQL Server 2008 Locking
Here are some notes on "SQL Server 2008 Locking" I took while attending an advanced class on SQL Server taught by Kimberly Tripp (https://sqlskills.com/AboutKimberlyLTripp.asp).
Please note that, although these notes were taken during the class, I might have added some of my own (mis)interpretation :-). Always check your facts on Books Online (I try to provide links when applicable). As with anything else you get from a blog, never use any of this in production before you thoroughly validate it a test environment. Please keep in mind that some of those will be hard to follow without the right pre-requisite knowledge and the right context. Reading the post from top to bottom will help.
You have more locks than you think
- You have a shared database lock every time you connect
- This helps someone from making the database read only while you're on
- One update on one row will give you three additional locks
- Table intent exclusive, page intent exclusive and row exclusive lock
- That's already 4 locks
- Use sp_lock @@spid to figure out what's going on
Row locks
- Shared locks, Update locks, eXclusive locks
- S on S = OK, S on U = OK, S on X = WAIT
- U on S = OK, U on U = WAIT, U on X = WAIT
- X on S = WAIT, X on U = WAIT, X on X = WAIT
Update locks
- Updates locks are there to help with concurrency
- U acquired in the beginning to isolate rows before change
- U does not prevent readers from seeing the data
- U updates to X on the row to do the update
Page and Table Locks
- Shared, Intent Shared, Intent eXclusive, Shared Intent Exclusive, eXclusive
- X on anyting - WAIT, IX on S - WAIT
Not necessarily accurate
- You're running SELECT (*) on a table with a clustered index
- The first row can be updated to be the last row
- You can actually end up count that row twice
- You're not reading anything that's not COMMITTED
- Better concurrency at the trade off of accuracy
WAIT
- If transactions are efficient and short, it's OK
- If you end up in a WAIT, SQL will WAIT
- SQL Server is incredibily patient :-)
- This also causes your transaction log to grow...
- You can write code to look at who is WAITing on what
- You can write code to kill the transaction that's causing you to WAIT
- However, it's best to figure out why that transaction is taking so long
- It could be an application transaction with external dependencies
Workarounds
- SET LOCK_TIMEOUT is an option at the session level, but be careful
- Use DBCC USEROPTIONS to get your session setting
- Use "SELECT * from sys.dm_exec_sessions" to get other session settings
- SELECT ... WITH (READPAST) hint is an option, but it skips entire pages
- With READPAST, make sure you don't care about missing a few rows
Granularity/Escalation
- SQL tries to be as granular as possible
- When there are too many locks, SQL locks higher in the hierarchy
- Escalation: From Row (RID) to Page to Partition to Table (object) to Database
- Lock escalation to a Partition lock is new in SQL Server 2008
- Change with ALTER TABLE table SET (LOCK_ESCALATION={AUTO|TABLE|DISABLE})
- Monitor with SELECT * FROM sys.dm_tran_locks
Hints to control locks
- You can use ROWLOCK, PAGLOCK, TABLOCK, UPDLOCK, XLOCK
- Careful. This can reduce perfromance or increase concurrency.
- You can do "UPDATE table SET @@var=column=<exp>" to avoid a reselect
- Look at ISOLATION LEVEL more than these types of locks
- Books Online - https://msdn.microsoft.com/en-us/library/ms187373.aspx
- Details at https://www.sqlmag.com/articles/index.cfm?articleid=6198&
Isolation Levels
- This is the I in the ACID properties, controlled with locks
- Level 0 - read uncommited
- Level 1 - read commited - This is the default!
- Level 2 - repeatable read
- Level 3 - serializable
- SET TRANSACTION ISOLATION LEVEL <string>
Other ways to separate readers/writers
- Backup/Restore, SQL IS, Replication, SQL AS, Snapshots
- For real-time, mixed, live workloads - only with snapshot isolation
Locks and issues
- Level 0 - Locks: No row locks, locks not honored
- Level 0 - Issue: Dirty reads
- Level 1 - Locks: Reader locks released ASAP. SELECT (*) locks per row.
- Level 1 - Issue: Inconsistent Analysis, Non-repeatable reads (data can change during transaction)
- Level 2 - Locks: Reader locks are left behind. SELECT (*) locks all.
- Level 2 - Issue: Phantoms (new rows can show up)
- Level 3 - Locks: Locks on ranges (table locks if no index)
READ_COMMITED_SNAPSHOT
- Starting in SQL Server 2005, database option for READ_COMMITED_SNAPSHOT
- Level 1, uses versioning instead of locking
- Can be overridden with READCOMMITEDLOCK hint
Isolation with table hints
- Level 0 - READUNCOMMITTED, NOLOCK
- Level 1 - READCOMMITTED
- Level 2 - REPEATABLEREAD, READCOMMITTEDLOCK (with RCS option on)
- Level 3 - SERIALIZABLE, HOLDLOCK
- SELECT...FROM table1 WITH (READUNCOMMITED) JOIN table2 WITH (SERIALIZABLE)
Blocking
- Locking is good. Guarantees consistency. Prevents conflicts.
- Blocking is not good. It happens if there's a transaction taking too long.
- Find the culprit, fix the issue
Tips to minimize blocking
- Keep transactions short
- Keep transactions in one batch, no interactions
- Use indexes to lock only the necessary data
- Consider estimates instead of long running queries
- Consider separate analysis services for long running queries
- Look closely at long running or conflicting transactions
Detecting Blocking
- Look at sp_blocker_pss80 from https://support.microsoft.com/?id=271509
- Tells who's in the head of the chain
- Deadlock events and trace flag 1222
- Performance data collection and extended events
- Use sp_who3 (not in the product, search the web)
- SQL Profiler, Performance Monitor, Extended Events
Deadlocks
- Two or more transactions request mutually desired resources in wrong order
- It's some sort of circular references, many specific cases.
- Classic case: spose moving savings to checking, other spouse moving checking to saving
- Happens only if you lock the same rows in the reverse sequence, at the same time
- SQL will identify those and kill the "least expensive" transaction
- SQL Profiler events, good for quick visual of what was happening
- You can save the XML format with XDL extension, sent to the developer
- Extended events, Trace flags flags to send to SQL log
Deadlock resolution
- You can use SET DEADLOCK_PRIORITY LOW (choose me, choose me)
- Since SQL Server 2005, you can also do SET DEADLOCK_PRIORITY HIGH (don't choose me)
- Can use a RANGE from -10 to 10 instead of LOW (-5), MEDIUM (0), HIGH (5)
- Deadlock victim receives error 1205
- Books online: https://msdn.microsoft.com/en-us/library/ms186736.aspx
- Certain distributed or application deadlock might be impossible to detect by SQL
Deadlock avoidance
- Access resources in the same order
- Minimize blocking
- Change isolation level
Snapshot Isolation
- Introduced in SQL Server 2005
- There is a cost to implementing this (versioning)
- Much better option then leaving transactions waiting around
Extra space needed for versioning
- Older version of the row stored in tempdb as rows are updated
- 14-byte pointer (LSN) to old version added to row in the data page
- Carefull with splitting if you turn this on and data pages are full
- Pointer in the row in the data page does not go away
Statement-level read consistency
- Read committed isolation using row versioning (aka RCSI)
- Database option: ALTER DATABASE SET READ_COMMITTED_SNAPSHOT ON
- Consider using the "WITH ROLLBACK AFTER 5" option
- You can see the database schema modification log
- This turns it on and uses it for all statements
- See https://msdn.microsoft.com/en-us/library/ms345124.aspx
Transaction-level read consistency
- Snapshot isolation
- Database option: ALTER DATABASE SET ALLOW_SNAPSHOT_ISOLATION ON
- Makes it available, but only uses it if requested in transaction
- Session setting: SET TRANSACTION ISOLATION LEVEL SNAPSHOT
- This is per user (session) and tracks it per transaction
- This potentially holds the versions in tempdb for longer
- Different transactions updating same record detected
- See https://msdn.microsoft.com/en-us/library/ms179599.aspx
- Four choices: None (default), Statement, Transaction, Both