SQL Server, il fuorviante XLOCK & ottimizzazioni
-
25-09-2019 - |
Domanda
Da alcune recenti test e la lettura che ho fatto, sembra che la "X" (esclusiva) Nome parte XLOCK è fuorviante. Essa, infatti, non si blocca più di quanto UPDLOCK. Se fosse esclusiva, sarebbe prevenire SELECT esterni, che non è così.
Non riesco a vedere né dalla lettura o dalla sperimentazione e la differenza tra i due.
L'unica XLOCK tempo crea un blocco esclusivo è quando viene utilizzato con TABLOCK. La mia prima domanda è "perché solo in questo granularità?"
Inoltre, mi sono imbattuto in un blog che afferma quanto segue :
Tuttavia, attenzione per XLOCK suggerimento. SQL Server ignorerà efficacemente XLOCK suggerimento! C'è un'ottimizzazione dove il check SQL Server se i dati è cambiato da quando la più vecchia transazione aperta. Se no, allora un xlock viene ignorato. Questo rende i suggerimenti xlock sostanzialmente inutile e dovrebbe essere evitato.
Qualcuno ha eseguito attraverso questo fenomeno?
In base a quello che sto vedendo, sembra che questo suggerimento dovrebbe essere ignorato.
Soluzione
Più esclusivo di serrature X
vs U
serrature
Nel matrice di compatibilità serratura sotto si può vedere che il blocco X
è compatibile solo con la stabilità schema e tipi di blocco Inserire Range-Null. U
è compatibile con i seguenti ulteriori tipi di blocco condivise S
/ IS
/ RS-S
/ RI-S
/ RX-S
granularità di X
serrature
Questi sono presi tutto bene a tutti i livelli. La sceneggiatura e profiler traccia seguente dimostra che vengano prese con successo a livello di riga.
CREATE TABLE test_table (id int identity(1,1) primary key, col char(40))
INSERT INTO test_table
SELECT NEWID() FROM sys.objects
select * from test_table with (rowlock,XLOCK) where id=10
Ma le righe si può ancora leggere!
Si scopre che a livello di isolamento read committed
SQL Server non sempre togliere le serrature S
, si salta questo passaggio se non v'è alcun rischio di leggere dati commit senza . Questo significa che non v'è alcuna garanzia di un conflitto di blocco che si verificano mai.
Tuttavia, se le iniziali Select è with (paglock,XLOCK)
allora questo fermare la transazione di lettura come il blocco X
sulla pagina bloccherà il blocco di pagina IS
che sarà sempre necessaria da parte del lettore. Ciò, naturalmente, hanno un impatto sulla concorrenza.
Altro Avvertimenti
Anche se si blocca la riga / pagina di questo non significa che si bloccano tutti gli accessi a quella riga della tabella. Un blocco su una riga nell'indice cluster non impedirà query lettura dati della riga corrispondente in un indice di copertura non cluster.
Altri suggerimenti
Non è un avvertimento, è un malinteso su ciò che accade nella SELECT.
Un semplice SELEZIONA non richiede blocchi condivisi se le pagine non contengono dati sporchi, e quindi non è bloccato da XLOCK.
Per essere bloccata da XLOCK, è necessario eseguire nel livello di isolamento REPEATABLE LEGGI. Due cose che possono innescare:
- Dati Modifica, attraverso INSERT / UPDATE / DELETE. La tabella aggiornata non deve essere quello della XLOCK è acceso.
- chiedendo esplicitamente REPEATABLE LEGGI attraverso il livello di isolamento delle transazioni o hint di tabella.
sulla base dei commenti a @ di Martin risposta , ecco un piccolo script (eseguito le diverse parti in diversi SSMS finestre per testare il blocco che impedisce un SELECT:
--
--how to lock/block a SELECT as well as UPDATE/DELETE on a particular row
--
--drop table MyTable
--set up table to test with
CREATE TABLE MyTable (RowID int primary key clustered
,RowValue int unique nonclustered not null)
--populate test data
;WITH InsertData AS
(
SELECT 4321 AS Number
UNION ALL
SELECT Number+1
FROM InsertData
WHERE Number<9322
)
INSERT MyTable
(RowID,RowValue)
SELECT
Number, 98765-Number
FROM InsertData
ORDER BY Number
OPTION (MAXRECURSION 5001)
-----------------------------------------------------------------------------
-- #1
--OPEN A NEW SSMS window and run this
--
--create lock to block select/insert/update/delete
DECLARE @ID int
BEGIN TRANSACTION
SELECT @ID=RowID FROM MyTable WITH (ROWLOCK, XLOCK, HOLDLOCK) WHERE RowID=6822
PRINT @ID
--COMMIT --<<<only run the commit when you want to release the lock
--<<<adfter opening the other new windows and running the SQL in them
-----------------------------------------------------------------------------
-- #2
--OPEN A NEW SSMS window and run this
--
--shows how a select must wait for the lock to be released
--I couldn't get SSMS to output any text while in the trnasaction, even though
--it was completing those commands (possibly buffering them?) so look at the
--time to see that the statements were executing, and the SELECT...WHERE RowID=6822
--was what was where this script is blocked and waiting
SELECT GETDATE() AS [start of run]
SELECT '1 of 2, will select row',* FROM MyTable Where RowID=6822
go
DECLARE @SumValue int
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SELECT GETDATE() AS [before transaction, shouldn't be nuch difference]
BEGIN TRANSACTION
SELECT @SumValue=SUM(RowID) FROM MyTable WHERE ROWID<6000
SELECT GETDATE() AS [in transaction, shouldn't be much difference]
, @SumValue AS SumValue
--everything to here will run immediately, but the select below will wait for the
-- lock to be removed
SELECT '2 of 2, will wait for lock',* FROM MyTable Where RowID=6822
SELECT GETDATE() AS [in transaction after lock was removed, should show a difference]
COMMIT
-----------------------------------------------------------------------------
-- #3
--OPEN A NEW SSMS window and run this
--
--show how an update must wait
UPDATE MyTable SET RowValue=1111 WHERE RowID=5000 --will run immediately
GO
UPDATE MyTable SET RowValue=1111 WHERE RowID=6822 --waits for the lock to be removed
-----------------------------------------------------------------------------
-- #4
--OPEN A NEW SSMS window and run this
--
--show how a delete must wait
DELETE MyTable WHERE RowID=5000 --will run immediately
go
DELETE MyTable WHERE RowID=6822 --waits for the lock to be removed