¿Qué hace específicamente OracleBulkCopy y cómo puedo optimizar su rendimiento?


14

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:

  1. ¿ OracleBulkCopyUtiliza la clase carga convencional o directa? ¿Hay alguna forma de confirmar esto, de una forma u otra?

  2. 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.

  3. 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é?

  4. 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?

  5. 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?

  6. ¿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?)

  7. 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?


Nota al margen: los datos que se copian ya están ordenados de acuerdo con el PK, que es el único índice en la tabla.
Aaronaught

¿Está utilizando un DataReader para leer los datos de la fuente?
bernd_k

@bernd_k: No, cargando completamente desde la memoria. Definitivamente no es la fuente ese es el problema.
Aaronaught

Respuestas:


13

Unos días más de lectura y experimentación y pude (principalmente) responder muchas de estas preguntas:

  1. Encontré esto enterrado en la documentación de ODP.NET (irónicamente, no en los OracleBulkCopydocumentos):

    La función de copia masiva de ODP.NET utiliza un enfoque de carga de ruta directa, que es similar a Oracle SQL * Loader, pero no lo mismo. Usar la carga de ruta directa es más rápido que la carga convencional (usando INSERTsentencias SQL convencionales ).

    Entonces parece que usa la ruta directa.

  2. Pude verificar esto haciendo una operación de copia masiva y obteniendo las propiedades de índice de SQL Developer. El índice no aparece como UNUSABLEmientras que la copia masiva estaba en marcha. Sin embargo , también descubrí que OracleBulkCopy.WriteToServerse negará a ejecutarse si el índice comienza en un UNUSABLEestado, 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.

  3. 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:

    Restricciones habilitadas
    Durante una copia masiva de Oracle, las siguientes restricciones se habilitan automáticamente de manera predeterminada:

    • NOT NULL
    • UNIQUE
    • PRIMARY KEY (restricciones únicas en columnas no nulas)

    NOT NULLlas restricciones se verifican en el momento de la construcción de la matriz de columnas Cualquier fila que viole la NOT NULLrestricción es rechazada.

    UNIQUElas restricciones se verifican cuando los índices se reconstruyen al final de la carga. El índice se deja en un estado de índice inutilizable si viola una UNIQUErestricción.

    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 OracleBulkCopyque felizmente le permitirá violar las restricciones del índice (y poner el índice en UNUSABLEestado cuando esté hecho), mi presentimiento es que está construyendo el índice PK durante la copia masiva pero simplemente no lo valida hasta después.

  4. 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.

  5. OracleBulkCopyarrojará una excepción si un índice está inicialmente en el UNUSABLEestado, por lo que es realmente un punto discutible.

  6. Si no hay otros factores, índices e índices (especialmente PK) siguen siendo los más importantes, como descubrí por:

  7. Creando una tabla temporal global con el mismo esquema (usando CREATE AS), luego copiando masivamente en la tabla temporal, y finalmente haciendo un viejo plano INSERTde la tabla temporal a la tabla real. Dado que la tabla temporal no tiene índice, la copia masiva ocurre muy rápido, y la final INSERTtambié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).


No estoy seguro de verificar las restricciones de la clave principal. Tuve la oportunidad de insertar masivamente los mismos datos en una tabla de Oracle 2 veces y Seleccionar * muestra 2 las filas duplicadas. En ese estado, Eliminar no es posible, pero la tabla truncada ayuda a volver a un estado limpio.
bernd_k

@bernd_k: Deleteno es posible porque el índice sí lo es UNUSABLE. Ese es el resultado de la verificación de restricciones que ocurre al final de la copia masiva.
Aaronaught

Tengo un skript de PowerShell ejecutándose, llamando a la copia masiva a una base de datos Oracle desde un lector de datos de SQL Server, todas las tablas de destino con claves principales y no tengo problemas con tablas con hasta 205278 filas. Pero tengo mucho cuidado de llenar primero las tablas maestras antes de llenar las tablas de detalles. No eliminé ninguno de los otros índices en la tabla y no tengo problemas cuando las tablas están inicialmente vacías.
bernd_k

@bernd_k: Sí, tampoco tuve muchos problemas en ese volumen (ver mi último párrafo). Es cuando alcanzas los millones que se pone horrible. También puede haber una diferencia si vacía la tabla en algún momento después de cada copia masiva (esta no se vacía, se agrega, y usted sabe cómo los índices se vuelven más lentos a medida que se hacen más grandes).
Aaronaught

Tal vez ayuda cuando haces unalter session set skip_unusable_indexes = true;
Wernfried Domscheit

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.