web-dev-qa-db-fra.com

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

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 cliente de base pour tester différentes approches avec. J'obtiens ce qui semble être un verrou de table pendant les instructions INSERT INTO Et 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, je lance SELECT * FROM LAYOUTSv2. Dans les deux cas, lorsque le thread client est en pause (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 groupé, les verrous de page et les verrous de ligne étant tous deux 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 il est bien mis en garde ici et ailleurs, ce n'est pas ce que je devrais faire. J'ai essayé de mettre l'indicateur ROWLOCK sur les instructions INSERT et 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 qui est INSERTed. Avant de valider un UPDATE, les requêtes provenant d'autres threads lisent la version de départ de la ligne en cours de UPDATEed. 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.

13
John Riehl

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

Il verrouille une ligne du tableau mais votre SELECT * FROM LAYOUTSv2 essaie de lire la table entière, il est donc nécessairement bloqué par ce verrou.

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

Si vous configurez la base de données pour lire l'isolement de capture instantanée validée cela donnera l'effet souhaité pour les deux cas (au détriment d'une plus grande utilisation de tempdb)

10
Martin Smith

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

7
Suraj