Comment obtenir l'insertion et / ou la mise à jour SQL pour ne pas verrouiller la table entière sur MS SQL Server

13

Très novice sur le travail DB, alors appréciez votre patience avec une question de base. J'exécute SQL Server 2014 sur ma machine locale et j'ai une petite table et une application client de base pour tester différentes approches avec. J'obtiens ce qui semble être un verrou de table pendant les instructions INSERT INTOet UPDATE. Le client est une application ASP.NET avec le code suivant:

OleDbConnection cn = new OleDbConnection("Provider=SQLNCLI11; server=localhost\\SQLEXPRESS; Database=<my db>; user id=<my uid>; password=<my pwd>");
cn.Open();
OleDbTransaction tn = cn.BeginTransaction();
OleDbCommand cmd = new OleDbCommand("INSERT INTO LAYOUTSv2 (LAYOUTS_name_t, LAYOUTS_enabled_b, LAYOUTS_data_m) VALUES ('name', '-1', 'data')", cn, tn);
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT SCOPE_IDENTITY()";
int newkey = Decimal.ToInt32((decimal)cmd.ExecuteScalar());
Console.WriteLine("Created index " + newkey);
Thread.Sleep(15000);
tn.Commit();
tn = cn.BeginTransaction();
cmd.CommandText = "UDPATE LAYOUTSv2 SET LAYOUTS_enabled_b='-3' WHERE LAYOUTS_key='" + newkey + "'";
cmd.Transaction = tn;
cmd.ExecuteNonQuery();
Console.WriteLine("updated row");
Thread.Sleep(15000);
tn.Rollback();
cn.Close();

Je lance ce code, puis depuis le studio de gestion que je dirige SELECT * FROM LAYOUTSv2. Dans les deux cas, lorsque le thread client est suspendu (c'est-à-dire avant la validation / restauration), la requête SELECT se bloque jusqu'à ce que la validation / restauration se produise.

La table a le champ LAYOUTS_key affecté comme clé primaire. Dans la fenêtre des propriétés, il montre qu'il est unique et en cluster, avec les verrous de page et les verrous de ligne autorisés. Le paramètre d'escalade de verrouillage pour la table est Désactiver ... J'ai essayé les deux autres paramètres disponibles de Table et AUTO sans aucun changement. J'ai essayé SELECT ... WITH (NOLOCK)et cela renvoie un résultat immédiatement, mais comme cela est bien mis en garde ici et ailleurs, ce n'est pas ce que je devrais faire. J'ai essayé de ROWLOCKdonner un indice aux déclarations INSERTet UPDATE, mais rien n'a changé.

Le comportement que je recherche est le suivant: avant de valider un INSERT, les requêtes provenant d'autres threads lisent toutes les lignes sauf celle en cours de INSERTmodification. Avant de valider une UPDATErequête à partir d'autres threads, lisez la version de départ de la ligne en cours d' UPDATEédition. Existe-t-il un moyen de le faire? Si je dois fournir d'autres informations pour clarifier mon cas d'utilisation, veuillez me le faire savoir. Merci.

John Riehl
la source
3
Soit dit en passant WHERE LAYOUTS_key='" + newkey + "'est un non-non complet pour diverses raisons, y compris l'injection SQL, vous devez utiliser des requêtes paramétrées.
Martin Smith
1
@MartinSmith Merci pour l'avertissement à ce sujet ... jamais entendu parler de requêtes paramétrées ou d'attaques par injection SQL.
John Riehl
@JohnRiehl, re: attaques par injection, imaginez si votre utilisateur définit newkey" something';DELETE FROM LAYOUTSv2 --". Votre mise à jour se terminerait correctement, puis viderait la table car l'utilisateur a manipulé la requête en insérant une apostrophe. Normalement, une requête paramétrée ressemble à quelque chose UDPATE LAYOUTSv2 SET LAYOUTS_enabled_b='-3' WHERE LAYOUTS_key=?, après quoi vous attribuez séparément des valeurs au ?(le paramètre) dans votre code.
Daniel Hutmacher

Réponses:

10

Il y a de fortes chances qu'il ne verrouille pas la "table entière".

Il verrouille une ligne dans le tableau mais votre SELECT * FROM LAYOUTSv2essaie de lire le tableau entier est donc nécessairement bloqué par ce verrou.

Pour le cas d'insertion, vous pouvez simplement spécifier le READPASTconseil pour ignorer la ligne verrouillée, mais cela ne donnera pas le résultat souhaité pour le UPDATEcas (il sautera à nouveau la ligne sans lire la version de départ de la ligne).

Si vous configurez la base de données pour l' isolement des instantanés validés en lecture, cela donnera l'effet souhaité dans les deux cas (au détriment d'une utilisation accrue de tempdb)

Martin Smith
la source
J'ai changé "Is Read Committed Snapshot On" en True et maintenant cela fonctionne parfaitement sans aucun conseil. Merci! Un suivi ... J'ai laissé "Autoriser l'isolement de l'instantané" défini sur Faux ... est-ce OK? Merci.
John Riehl
@JohnRiehl - Oui si vous n'utilisez pas explicitement l' SNAPSHOTisolement pour le laisser désactivé, puis activez-le si vous décidez par la suite que cela vous serait utile.
Martin Smith
7

Les instructions d'insertion et de mise à jour sont censées créer des verrous au niveau des lignes. Toutefois, lorsque le nombre de verrous dans une transaction est de 5 000 ou plus, une escalade de verrous se produit et crée un verrou au niveau de la table. Veuillez voir ci-dessous.

https://technet.microsoft.com/en-us/library/ms184286(v=sql.105).aspx

Suraj
la source
Pas pertinent pour cette question car les instructions INSERT et UPDATE écrivent une seule ligne
Martin Smith