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:
La
OracleBulkCopy
classe utilise-t-elle un chargement conventionnel ou direct? Existe-t-il un moyen de confirmer cela, d'une manière ou d'une autre?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.
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?
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?
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?
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?)
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?
Réponses:
Encore quelques jours de lecture et d'expérimentation et j'ai pu (surtout) répondre à beaucoup d'entre eux:
J'ai trouvé cela enfoui dans la documentation ODP.NET (ironiquement pas dans les
OracleBulkCopy
documents):Il semble donc qu'il n'utiliser le chemin direct.
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
UNUSABLE
pendant que la copie en vrac était en cours. Cependant , j'ai également découvert queOracleBulkCopy.WriteToServer
refusera de s'exécuter si l'index démarre dans unUNUSABLE
é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.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:
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
OracleBulkCopy
vous permettra avec plaisir de violer les contraintes d'index (et de mettre l'index enUNUSABLE
é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.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.OracleBulkCopy
lèvera une exception si un index est initialement dans l'UNUSABLE
état, c'est donc vraiment un point discutable.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:
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 simpleINSERT
de 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 finaleINSERT
est é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).
la source
Delete
n'est pas possible car l'index l'estUNUSABLE
. Cela résulte de la vérification des contraintes qui se produit à la fin de la copie en bloc.alter session set skip_unusable_indexes = true;
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
la source