Nous tentons de mettre à jour / supprimer un grand nombre d'enregistrements dans une table de plusieurs milliards de lignes. Puisqu'il s'agit d'un tableau populaire, il y a beaucoup d'activité dans les différentes sections de ce tableau. Toute activité de mise à jour / suppression importante est bloquée pendant de longues périodes (car elle attend d'obtenir des verrous sur toutes les lignes ou le verrouillage de page ou le verrouillage de table), ce qui entraîne des délais d'attente ou plusieurs jours pour terminer la tâche.
Donc, nous changeons l'approche pour supprimer un petit lot de lignes à la fois. Mais nous voulons vérifier si les lignes sélectionnées (disons 100 ou 1000 ou 2000 lignes) sont actuellement verrouillées par un processus différent ou non.
- Sinon, procédez à la suppression / mise à jour.
- S'ils sont verrouillés, passez au groupe d'enregistrements suivant.
- À la fin, revenez au début et essayez de mettre à jour / supprimer ceux qui ont été laissés de côté.
Est-ce faisable?
Merci, ToC
Réponses:
Si je comprends bien la demande, l'objectif est de supprimer des lots de lignes, tandis qu'en même temps, des opérations DML se produisent sur les lignes de la table. Le but est de supprimer un lot; cependant, si des lignes sous-jacentes contenues dans la plage définie par ledit lot sont verrouillées, nous devons ignorer ce lot et passer au lot suivant. Nous devons ensuite revenir à tous les lots qui n'ont pas été précédemment supprimés et réessayer notre logique de suppression d'origine. Nous devons répéter ce cycle jusqu'à ce que tous les lots de lignes requis soient supprimés.
Comme cela a été mentionné, il est raisonnable d'utiliser une indication READPAST et le niveau d'isolement READ COMMITTED (par défaut), afin d'ignorer les plages passées pouvant contenir des lignes bloquées. J'irai plus loin et recommanderai d'utiliser le niveau d'isolement SERIALISABLE et les suppressions de grignotage.
SQL Server utilise des verrous de plage de clés pour protéger une plage de lignes implicitement incluses dans un jeu d'enregistrements lu par une instruction Transact-SQL lors de l'utilisation du niveau d'isolation de transaction sérialisable ... en savoir plus ici: https://technet.microsoft.com /en-US/library/ms191272(v=SQL.105).aspx
Avec les suppressions de grignotage, notre objectif est d'isoler une plage de lignes et de garantir qu'aucune modification ne se produira sur ces lignes pendant que nous les supprimons, c'est-à-dire que nous ne voulons pas de lectures ou d'insertions fantômes. Le niveau d'isolement sérialisable est destiné à résoudre ce problème.
Avant de présenter ma solution, je voudrais ajouter que je ne recommande pas de basculer le niveau d'isolement par défaut de votre base de données sur SERIALIZABLE ni de recommander que ma solution soit la meilleure. Je souhaite simplement le présenter et voir où nous pouvons aller d'ici.
Quelques notes d'entretien:
Pour commencer mon expérience, je vais mettre en place une base de données de test, un exemple de table, et je remplirai la table de 2 000 000 de lignes.
À ce stade, nous aurons besoin d'un ou plusieurs index sur lesquels les mécanismes de verrouillage du niveau d'isolement SERIALIZABLE peuvent agir.
Maintenant, vérifions que nos 2 000 000 de lignes ont été créées
Nous avons donc notre base de données, notre table, nos index et nos lignes. Alors, configurons l'expérience pour supprimer les suppressions. Tout d'abord, nous devons décider de la meilleure façon de créer un mécanisme de suppression de grignotage typique.
Comme vous pouvez le voir, j'ai placé la transaction explicite dans la boucle while. Si vous souhaitez limiter les débits de journaux, n'hésitez pas à le placer en dehors de la boucle. De plus, étant donné que nous sommes dans le modèle de récupération COMPLET, vous souhaiterez peut-être créer des sauvegardes du journal des transactions plus souvent lors de l'exécution de vos opérations de suppression de grignotage, afin de vous assurer que votre journal des transactions ne puisse pas croître de manière scandaleuse.
Donc, j'ai quelques objectifs avec cette configuration. Tout d'abord, je veux mes verrous de plage de clés; donc, j'essaie de garder les lots aussi petits que possible. Je ne veux pas non plus avoir un impact négatif sur la concurrence sur ma table "gigantesque"; donc je veux prendre mes serrures et les laisser le plus vite possible. Je vous recommande donc de réduire la taille de vos lots.
Maintenant, je veux fournir un exemple très court de cette routine de suppression en action. Nous devons ouvrir une nouvelle fenêtre dans SSMS et supprimer une ligne de notre tableau. Je vais le faire dans une transaction implicite en utilisant le niveau d'isolement READ COMMITTED par défaut.
Cette ligne a-t-elle été supprimée?
Oui, il a été supprimé.
Maintenant, pour voir nos verrous, ouvrons une nouvelle fenêtre dans SSMS et ajoutons un ou deux extraits de code. J'utilise sp_whoisactive d'Adam Mechanic, qui peut être trouvé ici: sp_whoisactive
Maintenant, nous sommes prêts à commencer. Dans une nouvelle fenêtre SSMS, commençons une transaction explicite qui tentera de réinsérer la ligne que nous avons supprimée. En même temps, nous lancerons notre opération de suppression de grignotage.
Le code d'insertion:
Commençons les deux opérations en commençant par l'insertion et suivies de nos suppressions. Nous pouvons voir les serrures à clés et les serrures exclusives.
L'insert a généré ces verrous:
La suppression / sélection de grignotage contient ces verrous:
Notre insert bloque notre suppression comme prévu:
Maintenant, commettons la transaction d'insertion et voyons ce qui se passe.
Et comme prévu, toutes les transactions sont terminées. Maintenant, nous devons vérifier si l'insertion était un fantôme ou si l'opération de suppression l'a également supprimée.
En fait, l'encart a été supprimé; ainsi, aucun insert fantôme n'était autorisé.
Donc, en conclusion, je pense que la véritable intention de cet exercice n'est pas d'essayer de suivre chaque verrou de ligne, de page ou de table et de déterminer si un élément d'un lot est verrouillé et nécessiterait donc notre opération de suppression pour attendez. C'était peut-être l'intention des intervenants; cependant, cette tâche est herculéenne et pratiquement impossible, voire impossible. Le véritable objectif est de garantir qu'aucun phénomène indésirable ne se produit une fois que nous avons isolé la plage de notre lot avec des verrous qui nous sont propres, puis que nous supprimons le lot. Le niveau d'isolement SERIALISABLE atteint cet objectif. La clé est de garder vos petits morceaux, votre journal des transactions sous contrôle et d'éliminer les phénomènes indésirables.
Si vous voulez de la vitesse, ne construisez pas de tables gigantesques qui ne peuvent pas être partitionnées et ne pouvez donc pas utiliser la commutation de partition pour les résultats les plus rapides. La clé de la vitesse est le partitionnement et le parallélisme; la clé de la souffrance est le grignotage et le verrouillage des vies.
S'il vous plait, faite moi part de votre avis.
J'ai créé quelques exemples supplémentaires du niveau d'isolement SERIALIZABLE en action. Ils devraient être disponibles sur les liens ci-dessous.
Supprimer l'opération
Insérer une opération
Opérations d'égalité - Verrouillage de plage de clés sur les prochaines valeurs clés
Opérations d'égalité - extraction singleton de données existantes
Opérations d'égalité - extraction singleton de données inexistantes
Opérations d'inégalité - Verrouillage de plage de clés sur la plage et valeurs de clé suivantes
la source
C'est une très bonne idée de supprimer par petits lots ou morceaux prudents . J'ajouter un petit
waitfor delay '00:00:05'
et selon le modèle de récupération de la base de données - siFULL
, puis fairelog backup
et siSIMPLE
ensuite fairemanual CHECKPOINT
pour éviter les ballonnements du journal des transactions - entre les lots.Ce que vous dites n'est pas entièrement possible hors de la boîte (en gardant à l'esprit vos 3 points). Si la suggestion ci-dessus -
small batches + waitfor delay
ne fonctionne pas (à condition de faire des tests appropriés), vous pouvez utiliser lequery HINT
.Ne pas utiliser
NOLOCK
- voir kb / 308886 , Problèmes de cohérence de lecture de SQL Server par Itzik Ben-Gan , Mettre NOLOCK partout - Par Aaron Bertrand et SQL Server NOLOCK Hint & autres mauvaises idées .READPAST
un indice vous aidera dans votre scénario. L'essentiel de l'READPAST
indice est - s'il y a un verrou au niveau de la ligne, le serveur SQL ne le lira pas.Au cours de mes tests limités, j'ai trouvé un très bon débit lors de l'utilisation
DELETE from schema.tableName with (READPAST, READCOMMITTEDLOCK)
et de la définition du niveau d'isolement de la session de requête surREAD COMMITTED
using,SET TRANSACTION ISOLATION LEVEL READ COMMITTED
qui est de toute façon le niveau d'isolement par défaut.la source
Résumant d'autres approches initialement proposées dans les commentaires de la question.
À utiliser
NOWAIT
si le comportement souhaité consiste à faire échouer le bloc entier dès qu'un verrou incompatible est rencontré.De la
NOWAIT
documentation :Utilisez
SET LOCK_TIMEOUT
pour obtenir un résultat similaire, mais avec un délai d'expiration configurable:De la
SET LOCK_TIMEOUT
documentationla source
Pour supposer que nous avons 2 requêtes parallèles:
connect / session 1: verrouillera la ligne = 777
connect / session 2: ignorera la ligne verrouillée = 777
OU connect / session 2: lèvera une exception
la source
Essayez de filtrer sur quelque chose comme ça - cela peut devenir compliqué si vous voulez devenir vraiment très précis. Regardez dans BOL pour la description de sys.dm_tran_locks
la source
Vous pouvez utiliser NoLOCK pendant la suppression et si les lignes sont verrouillées, elles ne seront pas supprimées. Ce n'est pas idéal mais peut faire l'affaire pour vous.
la source
Msg 1065, Level 15, State 1, Line 15 The NOLOCK and READUNCOMMITTED lock hints are not allowed for target tables of INSERT, UPDATE, DELETE or MERGE statements.
, obsolète depuis 2005