2009-04-15 10 views
26

Intento insertar millones de registros en una tabla que tiene más de 20 índices.Insertar lento a granel para tabla con muchos índices

En la última ejecución se tardó más de 4 horas por cada 100.000 filas, y la consulta fue cancelada después de 3½ días ...

¿Tiene alguna sugerencia sobre cómo acelerar este proceso.

(sospecho que los muchos índices a ser la causa Si también piensa así, ¿cómo puedo soltar automáticamente índices antes de la operación, y luego crear los mismos índices más tarde de nuevo.?)

Información adicional:

  • el espacio utilizado por los índices es de aproximadamente 4 veces el espacio utilizado por los datos solos
  • los insertos se envuelven en una transacción por 100.000 filas.

actualización de la situación:

La respuesta aceptada me ayudó a hacer que sea mucho más rápido.

Respuesta

41

Puede deshabilitar y habilitar los índices. Tenga en cuenta que deshabilitarlos puede tener efectos secundarios no deseados (como tener claves primarias duplicadas o índices únicos, etc.) que solo se encontrarán al volver a habilitar los índices.

--Disable Index 
ALTER INDEX [IXYourIndex] ON YourTable DISABLE 
GO 

--Enable Index 
ALTER INDEX [IXYourIndex] ON YourTable REBUILD 
GO 
+8

Probablemente quiera no deshabilitar el índice PK (y definitivamente no si está en clúster). – Richard

+0

Sí, tienes razón. Gracias por señalar eso. – Lucero

+2

@Lucero: Gracias por esta respuesta. Una sugerencia: ¿quizás actualice su respuesta de acuerdo con el comentario de Richard? –

7

Esto suena como una operación de depósito de datos. Sería normal soltar los índices antes del inserto y reconstruirlos después.

Cuando vuelva a generar los índices, cree primero el índice agrupado y, a la inversa, suéltelo al final. Todos deberían tener fillfactor 100%.

Código debería ser algo como esto

if object_id('Index') is not null drop table IndexList 
select name into Index from dbo.sysindexes where id = object_id('Fact') 

if exists (select name from Index where name = 'id1') drop index Fact.id1 
if exists (select name from Index where name = 'id2') drop index Fact.id2   
if exists (select name from Index where name = 'id3') drop index Fact.id3 
. 
. 
BIG INSERT 

RECREATE THE INDEXES 
4

Como se ha señalado por otra respuesta incapacitantes índices serán un muy buen comienzo.

4 horas por cada 100.000 filas [...] Los insertos se envuelven en una transacción por 100.000 filas.

Usted debe mirar a la reducción del número, el servidor tiene que mantener una gran cantidad de estado, mientras que en una transacción (por lo que se puede deshacer), este (junto con los índices) significa la adición de los datos es muy difícil trabajo.

¿Por qué no ajustar cada instrucción de inserción en su propia transacción?

También observe la naturaleza del SQL que está utilizando, ¿está agregando una fila por declaración (y red de ida y vuelta) o agregando muchas?

+0

Gracias por la respuesta y por las preguntas adicionales. La inserción masiva está sucediendo con una sola llamada a un procedimiento almacenado. –

+1

Creo que un enlace a la otra respuesta usando simplemente href = "# 751062" evita la recarga de la página. –

+0

@Ole: gracias por la idea en el enlace de enlace (obvio en retrospectiva :-)). – Richard

3

Desactivar y luego volver a habilitar los índices se sugiere con frecuencia en esos casos. Tengo mis dudas sobre este enfoque, sin embargo, porque:

(1) El usuario de la base de datos de la aplicación necesita privilegios de alteración del esquema, que normalmente no debería tener. (2) En primer lugar, el esquema de inserción y/o índice insertado podría ser menor que óptimo, de lo contrario, la reconstrucción de árboles de índice completos no debería ser más rápida que una inserción de lotes decente (por ejemplo, el cliente publica una declaración de inserción a la vez, causando miles de viajes de ida y vuelta del servidor, o una mala elección en el índice agrupado, lo que lleva a divisiones de nodo de índice constantes).

Es por eso que mis sugerencias se ven un poco diferente:

  • Aumentar ADO.NET BatchSize
  • Elige índice agrupado de la tabla de destino con prudencia, de manera que los insertos no darán lugar a divisiones de nodos índice agrupado. Por lo general, una columna de identidad es una buena opción
  • Deje que el cliente inserte primero en una tabla de almacenamiento dinámico temporal (las tablas de almacenamiento dinámico no tienen ningún índice agrupado); a continuación, emita uno grande "insert-en-select" para empujar todo lo que los datos de la tabla puesta en escena en la tabla destino real
  • Aplicar SqlBulkCopy
  • el registro de transacciones Disminución por la elección de modelo de recuperación por medio de registros de

Usted podría encontrar información más detallada en this article.

Cuestiones relacionadas