Para resumir los detalles: necesitamos organizar aproximadamente 5 millones de filas en una base de datos de proveedor (Oracle). Todo funciona muy bien para lotes de 500k filas usando OracleBulkCopy
(ODP.NET), pero cuando tratamos de escalar hasta 5M, el rendimiento comienza a ralentizarse una vez que alcanza la marca de 1M, se vuelve progresivamente más lento a medida que se cargan más filas, y eventualmente tiempos de espera después de 3 horas más o menos.
Sospecho que está relacionado con una clave principal en la tabla, pero he estado pesca de arrastre de los foros de Oracle y desbordamiento de la pila de información y una gran cantidad de lo que estoy leyendo que contradice (también, una gran cantidad de mensajes parecen contradecir entre sí ) . Espero que alguien pueda dejar las cosas claras sobre algunas preguntas relacionadas con el proceso:
¿
OracleBulkCopy
Utiliza la clase carga convencional o directa? ¿Hay alguna forma de confirmar esto, de una forma u otra?Suponiendo que hace uso de vía directa de la carga: ¿Es cierto que Oracle establece automáticamente todos los índices a inutilizable durante la carga y los pone de nuevo en línea después? He leído varias declaraciones en este sentido, pero de nuevo, no puedo confirmarlo.
Si el n. ° 2 es verdadero, ¿debería hacer alguna diferencia qué índices hay en la tabla antes de iniciar una operación de copia masiva? Si es así, ¿por qué?
En relación con el n. ° 3, ¿hay alguna diferencia práctica, en general, entre la carga masiva con un índice inutilizable y la caída real del índice antes de la carga y volver a crearlo después?
Si el número 2 no es correcto, o si hay algunas advertencias que no entiendo, ¿haría alguna diferencia hacer explícitamente inutilizable el índice antes de la carga masiva y luego reconstruirlo explícitamente después?
¿Hay algo más, aparte de las compilaciones de índice, que pueda hacer que una operación de copia masiva disminuya progresivamente a medida que se agreguen más registros? (¿Tal vez algo relacionado con el registro, aunque esperaría que las operaciones masivas no se registren?)
Si realmente no hay otra forma de mejorar el rendimiento, además de descartar primero el PK / índice, ¿qué pasos puedo tomar para asegurarme de que el índice no desaparezca por completo, es decir, si la conexión a la base de datos se pierde en la mitad del proceso?
Respuestas:
Unos días más de lectura y experimentación y pude (principalmente) responder muchas de estas preguntas:
Encontré esto enterrado en la documentación de ODP.NET (irónicamente, no en los
OracleBulkCopy
documentos):Entonces parece que sí usa la ruta directa.
Pude verificar esto haciendo una operación de copia masiva y obteniendo las propiedades de índice de SQL Developer. El índice no aparece como
UNUSABLE
mientras que la copia masiva estaba en marcha. Sin embargo , también descubrí queOracleBulkCopy.WriteToServer
se negará a ejecutarse si el índice comienza en unUNUSABLE
estado, por lo que claramente hay más cosas aquí, porque si fuera tan simple como deshabilitar y reconstruir el índice, no debería importarle el estado inicial.Realmente hace una diferencia específicamente si el índice también es una restricción . Encontré esta pequeña joya en la documentación vinculada anteriormente:
La documentación es un poco confusa sobre lo que sucede durante la carga, especialmente con las claves primarias, pero una cosa es absolutamente segura: se comporta de manera diferente con una clave primaria frente a una sin ella . Dado
OracleBulkCopy
que felizmente le permitirá violar las restricciones del índice (y poner el índice enUNUSABLE
estado cuando esté hecho), mi presentimiento es que está construyendo el índice PK durante la copia masiva pero simplemente no lo valida hasta después.No estoy seguro de si la diferencia observada está dentro del propio Oracle o solo una peculiaridad del
OracleBulkCopy
. El jurado todavía está fuera de este.OracleBulkCopy
arrojará una excepción si un índice está inicialmente en elUNUSABLE
estado, por lo que es realmente un punto discutible.Si no hay otros factores, índices e índices (especialmente PK) siguen siendo los más importantes, como descubrí por:
Creando una tabla temporal global con el mismo esquema (usando
CREATE AS
), luego copiando masivamente en la tabla temporal, y finalmente haciendo un viejo planoINSERT
de la tabla temporal a la tabla real. Dado que la tabla temporal no tiene índice, la copia masiva ocurre muy rápido, y la finalINSERT
también es rápida porque los datos ya están en una tabla (todavía no he probado la sugerencia de agregar, ya que una copia de tabla a tabla de 5M filas ya lleva menos de 1 minuto).Todavía no estoy seguro de las posibles ramificaciones de (ab) usar el espacio de tabla temporal de esta manera, pero hasta ahora no me ha dado ningún problema, y es mucho más seguro que la alternativa para evitar la corrupción de las filas o índices.
El éxito de esto también demuestra claramente que el índice PK es el problema, ya que es la única diferencia práctica entre la tabla temporal y la tabla permanente, ambas comenzaron con cero filas durante las pruebas de rendimiento.
Conclusión: no se moleste en tratar de copiar masivamente más de 100k filas en una tabla indexada de Oracle usando ODP.NET. O bien, suelte el índice (si realmente no lo necesita) o "precargue" los datos en una tabla diferente (no indexada).
fuente
Delete
no es posible porque el índice sí lo esUNUSABLE
. Ese es el resultado de la verificación de restricciones que ocurre al final de la copia masiva.alter session set skip_unusable_indexes = true;
Aquí hay un artículo de Oracle que explica cuándo será beneficioso usar inserción masiva o cuándo no. Además, tiene una idea de lo que sucede a nivel de base de datos.
http://docs.oracle.com/cd/B28359_01/server.111/b28319/ldr_modes.htm
fuente