web-dev-qa-db-fra.com

Insertion et mise à jour «sans rapport».

Repro Scénario:

CREATE TABLE test (
  ID int IDENTITY(1,1),
  mykey nvarchar(255) NOT NULL,
  exp_date datetime,
PRIMARY KEY (ID));
GO

CREATE INDEX not_expired_keys ON test (exp_date, mykey);

INSERT INTO test (mykey, exp_date) VALUES ('A', NULL);

Je commence la transaction 1:

-- add key B
BEGIN TRANSACTION;
INSERT INTO test (mykey, exp_date) VALUES ('B', NULL);
...

puis exécuter une transaction 2 en parallèle:

-- expire key A
BEGIN TRANSACTION;
UPDATE test SET exp_date = GETDATE() WHERE exp_date IS NULL AND mykey = 'A'; -- <-- Blocking
ROLLBACK;

Comme il s'avère, la mise à jour d'insertion non engagée de transaction 1 est la mise à jour de la transaction 2, même s'ils affectent des ensembles disjoints de lignes (mykey = 'B' vs. mykey = 'A').

Observations:

  • Le blocage se produit également sur le niveau d'isolation des transactions le plus bas READ UNCOMMITTED.
  • Le blocage disparaît si je mets un index unique sur mykey. Malheureusement, je ne peux pas faire cela, car les noms de clés peuvent être réutilisés une fois qu'une clé a expiré.

Mes questions:

  • (Hors de curiosité :) Pourquoi ces déclarations se bloquent-elles même sur le READ UNCOMMITTED niveau?

  • Y a-t-il un moyen facile et fiable de ne pas les bloquer?

4
Heinzi

Permet de jeter un coup d'œil sur les plans d'exécution.

1ère requête - insert

BEGIN TRANSACTION;
INSERT INTO test (mykey, exp_date) VALUES ('B', NULL);

Et son plan d'exécution enter image description here

Nous voyons que SQL Server effectue une opération d'insertion d'index en cluster.

Permet maintenant de jeter un coup d'oeil sur la mise à jour

BEGIN TRANSACTION;
UPDATE test 
SET exp_date = GETDATE() 
WHERE exp_date IS NULL AND mykey = 'A' -- <-- Blocking

Et son plan d'exécution enter image description here

SQL Server scanne l'index en cluster de la table et mettez-le, même s'il peut choisir un autre index pour trouver les lignes correspondantes. La raison en est, car nous n'avons qu'une ligne dans la table et que SQL Server Optimizer cherche plus facilement à numériser l'index en clustere au lieu de rechercher des données dans l'index non en cluster.

Mais que si nous forçions le serveur SQL à utiliser un index non clustered?

BEGIN TRANSACTION;
UPDATE test 
SET exp_date = GETDATE() 
FROM test WITH(INDEX = not_expired_keys)
WHERE exp_date IS NULL AND mykey = 'A' -- <-- No Blocking!!!

Et son plan d'exécution enter image description here

Je pense que si nous mettons davantage de lignes dans la table SQL Server choisira l'index non clustered pour la recherche des lignes qui doivent être mises à jour et qu'il n'y aura pas de blocage.

4