2010-06-02 32 views
14

Digamos que tiene una tabla con aproximadamente 5 millones de registros y una columna nvarchar(max) poblada con datos de texto grandes. Desea establecer esta columna en NULL si SomeOtherColumn = 1 de la manera más rápida posible.La forma más rápida de realizar la actualización masiva

La fuerza bruta UPDATE no funciona muy bien aquí porque creará grandes transacciones implícitas y demorará para siempre.

Hacer actualizaciones en pequeños lotes de 50K registros funciona a la vez, pero aún demora 47 horas en completar el robusto servidor de 32 núcleos/64GB.

¿Hay alguna manera de hacer esta actualización más rápido? ¿Hay alguna sugerencia mágica/opciones de tabla que sacrifique algo más (como concurrencia) a cambio de velocidad?

NOTA: Crear una tabla temporal o una columna temporal no es una opción porque esta columna nvarchar(max) implica una gran cantidad de datos y, por lo tanto, consume mucho espacio.

PD: Sí, SomeOtherColumn ya está indexado.

+0

Vea también: http://stackoverflow.com/questions/571750/make-sql-server-faster-at-manipulating-data-turn-off-transaction-logging –

+0

¿Cómo está haciendo los registros de lotes '50K en una actualización de tiempo? ¿Es con un procedimiento almacenado? Si es así, ¿puedes poner el código? – Fede

+0

@ user356004: al volver a leer No puedo evitar pensar que su servidor está bajo mucha carga o que no está configurado correctamente: esos tiempos parecen muy altos. –

Respuesta

1

¿Ha intentado colocar un índice o estadísticas en someOtherColumn?

+0

Si el problema de rendimiento se debe a que no hay índice, por lo que se requiere una exploración de tabla para identificar las filas para actualizar, ¿no tardaría tanto tiempo (o más) en crear un nuevo índice y luego emitir la actualización? –

+0

¿Realmente es un índice en una columna nvarchar (max)? – Paparazzi

3

Puede establecer el modo de recuperación de la base de datos en Simple para reducir el registro, PERO no lo haga sin considerar todas las implicaciones para un entorno de producción.

¿Qué índices están en su lugar en la tabla? Dado que las actualizaciones por lotes de aprox. 50,000 filas tardan tanto, yo diría que requiere un índice.

0

Intente indexar 'SomeOtherColumn' ... Los registros de 50K deben actualizarse en un instante. Si ya existe un índice, verifique si es necesario reorganizar el índice y si se han recopilado las estadísticas correspondientes.

0

Si está ejecutando un entorno de producción sin suficiente espacio para duplicar todas sus tablas, creo que está buscando problemas tarde o temprano.

Si usted proporciona alguna información sobre el número de filas con SomeOtherColumn = 1, tal vez podemos pensar de otra manera, pero yo sugeriría:

0) Copia de seguridad de su mesa 1) Índice de la bandera columna 2) Conjunto la opción de tabla para "no registrar transferencias" ... si es posible 3) escribir un procedimiento almacenado para ejecutar las actualizaciones

+0

BTW ... ¿va a necesitar ejecutar este procedimiento más de una vez en la vida? –

+1

¿Cómo se configura la opción de tabla para "no registrar las transferencias"? – user356004

3

Afortunadamente ya ha eliminado los índices en la columna que está configurando como nulo, incluidos los índices de texto completo. Como se dijo anteriormente, desactivar las transacciones y el archivo de registro temporalmente sería suficiente. La copia de seguridad de sus datos también truncará sus archivos de registro.

+0

Definitivamente asegúrese de estar bajando índices. Ha acortado las cosas significativamente para mí en el pasado. –

1

Esto realmente me ha ayudado. Pasé de 2 horas a 20 minutos con esto.

/* I'm using database recovery mode to Simple */ 
/* Update table statistics */ 

set transaction isolation level read uncommitted  

/* Your 50k update, just to have a measures of the time it will take */ 

set transaction isolation level READ COMMITTED 

En mi experiencia, trabajando en MSSQL 2005, pasando todos los días (de forma automática) 4 millones 46-byte-Records (sin nvarchar (max), aunque) de una tabla en una base de datos a otra tabla en una base de datos diferente toma alrededor de 20 minutos en un servidor QuadCore de 8GB, 2Ghz y no afecta el rendimiento de la aplicación. Al moverme, quiero decir INSERT INTO SELECT y luego DELETE. El uso de la CPU nunca supera el 30%, incluso cuando la tabla que se está eliminando tiene 28 millones de registros y produce constantemente alrededor de 4 mil insertos por minuto, pero no actualizaciones. Bueno, ese es mi caso, puede variar dependiendo de la carga de tu servidor.

lectura no comprometida

"especifica que las declaraciones (actualizaciones) pueden leer filas que han sido modificadas por otras transacciones, pero aún no cometidos." En mi caso, los registros son de solo lectura.

No sé qué significa rg-tsql pero here encontrará información sobre niveles de aislamiento de transacciones en MSSQL.

+1

El "rg" es RedGate, una empresa patrocinadora que anuncia los resultados de la etiqueta [tsql]. – Corey

+1

Siempre tenga cuidado y asegúrese de comprender las implicaciones de leer transacciones no procesadas. Sí, su proceso no tendrá que esperar a que se confirmen las transacciones abiertas antes de eliminar los elementos, pero, por supuesto, si la transacción no se confirma después de todo esto, significará que eliminó la fila de forma incorrecta. – Cobusve

7

De todo lo que puedo ver, no parece que sus problemas estén relacionados con los índices.

La clave parece estar en el hecho de que su campo nvarchar (max) contiene "muchos" datos. Piense en lo que SQL tiene que hacer para realizar esta actualización.

Dado que la columna que está actualizando probablemente tenga más de 8000 caracteres, se almacena fuera de página, lo que implica un esfuerzo adicional en la lectura de esta columna cuando no es NULA.

Cuando ejecuta un lote de 50000 actualizaciones, SQL tiene que colocar esto en una transacción implícita para posibilitar la reversión en caso de problemas. Para revertir tiene que almacenar el valor original de la columna en el registro de transacciones.

Suponiendo (por simplicidad) que cada columna contiene un promedio de 10,000 bytes de datos, eso significa que 50,000 filas contendrán alrededor de 500MB de datos, que deben almacenarse temporalmente (en modo de recuperación simple) o permanentemente (en recuperación completa modo).

No hay forma de desactivar los registros, ya que comprometerá la integridad de la base de datos.

Realicé una prueba rápida en el escritorio lento de mi perro, y ejecutar lotes de incluso 10.000 se vuelve extremadamente lento, pero reducir el tamaño a 1000 filas, lo que implica un tamaño de registro temporal de 10 MB funcionó muy bien.

Cargué una tabla con 350,000 filas y marqué 50,000 para actualizar. Esto se completó en alrededor de 4 minutos, y como se escala linealmente, debería poder actualizar sus 5 millones de filas en mi escritorio lento para perros en alrededor de 6 horas en mi escritorio de 1GB de 2GB, así que esperaría algo mucho mejor en su fornido servidor respaldado por SAN o algo así.

Es posible que desee ejecutar su declaración de actualización como una selección, seleccionando solo la clave principal y la columna nvarchar grande, y asegúrese de que esto se ejecute tan rápido como esperaba.

Por supuesto, el cuello de botella puede ser que otros usuarios bloqueen cosas o contención en su almacenamiento o memoria en el servidor, pero como no mencionó a otros usuarios supondré que tiene la base de datos en modo de usuario único para esto.

Como optimización, debe asegurarse de que los registros de transacciones estén en un disco físico/grupo de discos diferente de los datos para minimizar los tiempos de búsqueda.

Cuestiones relacionadas