Que fait spécifiquement OracleBulkCopy et comment puis-je optimiser ses performances?

14

Pour résumer les détails: nous devons regrouper environ 5 millions de lignes dans une base de données fournisseur (Oracle). Tout va très bien pour les lots de 500 000 lignes utilisant OracleBulkCopy(ODP.NET), mais lorsque nous essayons de passer à 5 Mo, les performances commencent à ralentir à une analyse une fois qu'elles atteignent la marque 1 Mo, deviennent progressivement plus lentes à mesure que davantage de lignes sont chargées, et finalement expire après environ 3 heures.

Je pense qu'il est lié à une clé primaire sur la table, mais je l' ai été chalutait les forums de débordement Oracle et Stack pour informations et beaucoup de ce que je lis que contredit (aussi, beaucoup de messages semblent contredire les uns les autres ) . J'espère que quelqu'un pourra remettre les pendules à l'heure sur des questions étroitement liées au processus:

  1. La OracleBulkCopyclasse utilise-t-elle un chargement conventionnel ou direct? Existe-t-il un moyen de confirmer cela, d'une manière ou d'une autre?

  2. En supposant qu'il ne use-chemin direct chargement: Est - il vrai que Oracle définit automatiquement tous les index à inutilisable pendant la charge et met en ligne les récupérer par la suite? J'ai lu plusieurs déclarations à cet effet, mais encore une fois, je ne peux pas le confirmer.

  3. Si # 2 est vrai, cela devrait-il faire une différence quels sont les index sur la table avant de lancer une opération de copie en bloc? Si oui, pourquoi?

  4. En ce qui concerne le n ° 3, existe-t-il une différence pratique, en général, entre le chargement en bloc avec un index inutilisable et le fait de supprimer l'index avant le chargement et de le recréer ensuite?

  5. Si # 2 n'est pas correct, ou s'il y a des mises en garde que je ne comprends pas, cela ferait-il une différence de rendre explicitement l'index inutilisable avant le chargement en bloc, puis de le reconstruire explicitement après?

  6. Y a-t-il autre chose, autre que des générations d'index, qui pourrait ralentir progressivement une opération de copie en bloc à mesure que de plus en plus d'enregistrements sont ajoutés? (Peut-être quelque chose à voir avec la journalisation, même si je m'attendrais à ce que les opérations en bloc ne soient pas enregistrées?)

  7. S'il n'y a vraiment aucun autre moyen d'obtenir les performances à part de laisser tomber le PK / index en premier, quelles mesures puis-je prendre pour m'assurer que l'index ne disparaît pas complètement, c'est-à-dire si la connexion à la base de données est perdue dans au milieu du processus?

Aaronaught
la source
Note latérale: Les données en cours de copie sont déjà triées selon le PK, qui est le seul index de la table.
Aaronaught
Utilisez-vous un DataReader pour lire les données de la source?
bernd_k
@bernd_k: Non, chargement entièrement à partir de la mémoire. Ce n'est certainement pas la source du problème.
Aaronaught

Réponses:

13

Encore quelques jours de lecture et d'expérimentation et j'ai pu (surtout) répondre à beaucoup d'entre eux:

  1. J'ai trouvé cela enfoui dans la documentation ODP.NET (ironiquement pas dans les OracleBulkCopydocuments):

    La fonctionnalité ODP.NET Bulk Copy utilise une approche de chargement de chemin direct, qui est similaire, mais pas identique à Oracle SQL * Loader. L'utilisation d'un chargement de chemin direct est plus rapide que le chargement conventionnel (en utilisant des INSERTinstructions SQL conventionnelles ).

    Il semble donc qu'il n'utiliser le chemin direct.

  2. J'ai pu le vérifier en effectuant une grande opération de copie en bloc et en obtenant les propriétés d'index de SQL Developer. L'indice ne semble que UNUSABLEpendant que la copie en vrac était en cours. Cependant , j'ai également découvert que OracleBulkCopy.WriteToServerrefusera de s'exécuter si l'index démarre dans un UNUSABLEétat, il est donc clair qu'il se passe plus de choses ici, car si c'était aussi simple que de désactiver et de reconstruire l'index, il ne devrait pas se soucier de l'état initial.

  3. Cela fait une différence spécifiquement si l'index est également une contrainte . Trouvé ce petit bijou dans la documentation liée ci-dessus:

    Contraintes activées
    Lors d'une copie en bloc Oracle, les contraintes suivantes sont automatiquement activées par défaut:

    • NOT NULL
    • UNIQUE
    • PRIMARY KEY (contraintes uniques sur les colonnes non nulles)

    NOT NULLles contraintes sont vérifiées au moment de la construction du tableau de colonnes. Toute ligne qui viole la NOT NULLcontrainte est rejetée.

    UNIQUEles contraintes sont vérifiées lorsque les index sont reconstruits à la fin du chargement. L'index est laissé dans un état Index inutilisable s'il viole une UNIQUEcontrainte.

    La documentation est un peu floue sur ce qui se passe pendant le chargement, en particulier avec les clés primaires, mais une chose est absolument certaine - elle se comporte différemment avec une clé primaire ou sans . Étant donné que le OracleBulkCopyvous permettra avec plaisir de violer les contraintes d'index (et de mettre l'index en UNUSABLEétat une fois terminé), mon intuition est qu'il construit l'index PK pendant la copie en bloc mais ne le valide simplement qu'après.

  4. Je ne sais pas si la différence observée est à l'intérieur d'Oracle lui-même ou juste une bizarrerie du OracleBulkCopy. Le jury est toujours sur celui-ci.

  5. OracleBulkCopylèvera une exception si un index est initialement dans l' UNUSABLEétat, c'est donc vraiment un point discutable.

  6. S'il y a d' autres facteurs, des indices (et en particulier les indices PK) sont encore le plus important, comme je l' ai appris par:

  7. Création d'une table temporaire globale avec le même schéma (à l'aide de CREATE AS), puis copie en bloc dans la table temporaire, et enfin faire un ancien simple INSERTde la table temporaire dans la vraie table. Étant donné que la table temporaire n'a pas d'index, la copie en bloc se déroule très rapidement et la finale INSERTest également rapide car les données sont déjà dans une table (je n'ai pas encore essayé l'indicateur d'ajout, car une copie table à table de 5 millions de lignes prend déjà moins d'une minute).

    Je ne suis pas encore sûr des ramifications potentielles de (ab) en utilisant l'espace table temporaire de cette façon, mais jusqu'à présent, cela ne m'a pas posé de problème, et c'est beaucoup plus sûr que l'alternative en empêchant la corruption des deux lignes ou index.

    Le succès de cela démontre également assez clairement que l'index PK est le problème, car c'est la seule différence pratique entre la table temporaire et la table permanente - les deux ont commencé avec zéro ligne pendant les tests de performances.

Conclusion: ne vous embêtez pas à essayer de copier en bloc plus d'environ 100 000 lignes dans une table Oracle indexée à l'aide d'ODP.NET. Supprimez l'index (si vous n'en avez pas réellement besoin) ou "préchargez" les données dans une autre table (non indexée).

Aaronaught
la source
Je ne suis pas sûr de vérifier les contraintes de clé primaire. Je suis heureux d'insérer en vrac les mêmes données dans une table Oracle 2 fois et Select * affiche 2 les lignes dupliquées. Dans cet état, la suppression n'est pas possible, mais la table tronquée permet de revenir à un état propre.
bernd_k
@bernd_k: Deleten'est pas possible car l'index l'est UNUSABLE. Cela résulte de la vérification des contraintes qui se produit à la fin de la copie en bloc.
Aaronaught
J'ai un skript PowerShell en cours d'exécution, appelant la copie en bloc dans une base de données Oracle à partir d'un lecteur de données SQL Server, toutes les tables cibles avec des clés primaires et je n'ai aucun problème avec les tables avec jusqu'à 205278 lignes. Mais je fais très attention à remplir d'abord les tableaux principaux avant de remplir les tableaux de détails. Je n'ai supprimé aucun des autres index de la table et je n'ai aucun problème lorsque les tables sont initialement vides.
bernd_k
@bernd_k: Oui, je n'ai pas eu trop de problèmes à ce volume non plus (voir mon dernier paragraphe). C'est lorsque vous atteignez des millions de personnes que cela devient horrible. Il peut également y avoir une différence si vous videz la table quelque temps après chaque copie en bloc (celle-ci ne se vide pas, elle est ajoutée à, et vous savez comment les index ralentissent à mesure qu'ils grandissent).
Aaronaught
Peut-être que cela aide quand vous faites unalter session set skip_unusable_indexes = true;
Wernfried Domscheit
1

Voici un article d'Oracle qui explique quand il sera avantageux d'utiliser l'insertion en vrac ou non. A également un aperçu de ce qui se passe au niveau de la base de données.

http://docs.oracle.com/cd/B28359_01/server.111/b28319/ldr_modes.htm

SmartCoder
la source
3
Les liens ont tendance à pourrir; vous voudrez peut-être inclure des informations pertinentes à partir du lien dans votre réponse.
mustaccio