MISE À JOUR ou FUSION de très grandes tables dans SQL Server
-
13-11-2019 - |
Question
Je dois effectuer une mise à jour quotidienne d'un fichier très volumineux (300 millions d'enregistrements) et large TABLE1
.Les données sources des mises à jour se trouvent dans une autre table UTABLE
soit 10 % à 25 % des rangées de TABLE1
mais il est étroit.Les deux tableaux ont record_id
comme clé primaire.
Actuellement, je recrée TABLE1
en utilisant l'approche suivante :
<!-- language: sql -->
1) SELECT (required columns) INTO TMP_TABLE1
FROM TABLE1 T join UTABLE U on T.record_id=U.record_id
2) DROP TABLE TABLE1
3) sp_rename 'TMP_TABLE1', 'TABLE1'
Or cela prend près de 40 minutes sur mon serveur (60 Go de RAM pour SQL Server).Je souhaite obtenir un gain de performances de 50 % : quelles autres options puis-je essayer ?
MERGE
etUPDATE
- quelque chose comme le code ci-dessous ne fonctionne plus rapidement que pour un très petitUTABLE
table - en taille réelle, tout se bloque :<!-- language: SQL --> MERGE TABLE1 as target USING UTABLE as source ON target.record_id = source.record_id WHEN MATCHED THEN UPDATE SET Target.columns=source.columns
J'ai entendu dire que je pouvais effectuer une MERGE par lots en utilisant ROWCOUNT - mais je ne pense pas que cela puisse être assez rapide pour une table de 300 millions de lignes.
Des astuces de requête SQL qui peuvent être utiles ?
La solution
En fait, j'ai découvert des recommandations générales pour de telles requêtes :L'idée d'utiliser SQL Merge ou Update est très intelligente, mais elle échoue lorsque nous devons mettre à jour de nombreux enregistrements (c'est-à-dire 75M) dans une grande et large table (c'est-à-dire 240M).
En regardant le plan de requête de la requête ci-dessous, nous pouvons dire que TABLE SCAN
du TABLEAU1 et final MERGE
prennent 90% du temps.
MERGE TABLE1 as Target
USING UTABLE as source
ON Target.record_id = source.record_id
WHEN MATCHED AND (condition) THEN
UPDATE SET Target.columns=source.columns
Donc, pour utiliser MERGE, nous devons :
- Réduisez le nombre de lignes que nous devons mettre à jour et transmettez correctement ces informations à SQL Server.Cela peut être fait en faisant
UTABLE
plus petit ou en spécifiant descondition
cela rétrécit la partie à fusionner. - Assurez-vous que la partie à fusionner tient dans la mémoire, sinon la requête s'exécute beaucoup plus lentement.Fabrication
TABLE1
deux fois moins, j'ai réduit mon temps réel de requête de 11 heures à 40 minutes.
Comme Mark l'a mentionné, vous pouvez utiliser UPDATE
syntaxe et utilisation WHERE
clause pour restreindre la partie à fusionner - cela donnera les mêmes résultats.Veuillez également éviter d'indexer TABLE1
car cela entraînera un travail supplémentaire pour reconstruire l'index pendant MERGE
Autres conseils
Tout d'abord, je découvrirais où se trouve votre goulot d'étranglement : votre processeur est-il bloqué ou inactif ?En d’autres termes : votre sous-système IO est-il capable de gérer la charge correctement ?
Recréer la table complète représente beaucoup de charge d'E/S, sans compter que cela prendra beaucoup d'espace pour stocker la table deux fois temporairement.
Avez-vous besoin d'effectuer une MERGE - d'après ce que je peux voir, une simple mise à jour devrait suffire.Exemple:
UPDATE
TABLE1
SET
ColumnX = UTABLE.ColumnX
...
FROM
TABLE1
INNER JOIN
UTABLE ON TABLE1.record_id = UTABLE.record_id
Vous pouvez regrouper les mises à jour à l'aide de ROWCOUNT, mais cela n'accélérera pas l'exécution, cela contribuera uniquement à réduire le verrouillage global.
Aussi : quel type d’index avez-vous sur la table ?Il peut être plus rapide de désactiver les index avant la mise à jour, puis de les reconstruire à partir de zéro par la suite (uniquement les index non clusterisés).