Principales respuestas
¿Cómo funcionan los cambios de recovery model?

Pregunta
-
Buenas a todos una vez más. Recurro a ustedes con el siguiente escenario, pues si bien es cierto soy bastante diestro en los usos más comunes de SQL Server, carezco de experiencia en estos temas más orientados a un DBA, que no lo soy.
Tengo una sentencia UPDATE de la forma:
Update tabla Set Campo = t.Valor From ( SELECT interno que devuelve 1,1 millones de registros aprox. ) Where Condiciones ;
El UPDATE afecta la misma cantidad de registros que los devueltos por el SELECT interno. Tarda unos 27 minutos en correr. La sentencia no la creé yo pero sí me tocó examinarla por optimizaciones. Noté la falta de un índice y ahora la consulta está optimizada a más no poder. El SELECT interno que es bastante complejo y que calcula promedios basados en datos de 6 meses corría en 26 segundos y ahora lo hace en 4 segundos.
Puesto que el cálculo de los valores a usar y de los registros a actualizar están totalmente optimizados no veo razón alguna para tardar 27 minutos haciendo el UPDATE, excepto claro el hecho de tener que escribir 1,1 millones de actualizaciones en el transaction log.
Pensé entonces en hacer una prueba: Cambiar el recovery model a SIMPLE, luego correr el UPDATE, luego devolver el recovery model a FULL. Hice esto:
Alter Database BaseDatos Set Recovery Simple; Go UPDATE ... Go Alter Database BaseDatos Set Recovery Full; Go
Resultado: No hubo diferencia alguna. Es como si el cambio no se hubiera dado, aún cuando pude comprobar que sí había cambiado usando:
SELECT name AS [Database Name] , recovery_model_desc AS [Recovery Model] FROM sys.databases ;
La pregunta del millón sería: ¿Por qué no hubo cambio aparente en el desempeño del UPDATE? ¿Qué condiciones son necesarias para que un cambio de recovery model entre en efecto? De pronto estoy sospechando que no deben haber conexiones abiertas a la base de datos para que el cambio haga efecto, pero la verdad no tengo idea de si esto es así o no.
Entonces en resumen: ¿Qué estoy haciendo mal? ¿Por qué no logré un buen desempeño del UPDATE cambiando el recovery model a SIMPLE?
Jose R. MCP
Code Samplesdomingo, 28 de octubre de 2012 19:19
Respuestas
-
Hola Jose,
Cambiar el Recovery Model a SIMPLE no evita que las transacciones sean grabadas en el log. El efecto secundario de que el log no crece sucede porque en SIMPLE Recovery, el log es "truncated" en cada checkpoint. Esta es la razon por la que no puedes hacer backups del log cuando estas ne SIMPLE Recovery.
En FULL Recovery, el log recibe las transacciones, despues de un tiempo las transacciones son grabadas al disco y el log sigue grande hasta que haces un backup del log. Esto garantiza que puedas recuperar tus transacciones hasta cualquier punto en el tiempo restaurando el log.
En SIMPLE Recovery, el log recibe las transacciones, despues de un tiempo son grabadas al disco y el log es "truncated". Como las transacciones son eliminadas del log, ya no tiene sentido hacer backups del log y es asi que este proceso no es posible.
Saludos,
Monica
This posting is provided "AS IS" with no warranties, and confers no rights. Please remember to click "Mark as Answer" and "Vote as Helpful" on posts that help you. This can be beneficial to other community members reading the thread.- Propuesto como respuesta Ahias Portillo(FoxBurros)MVP domingo, 28 de octubre de 2012 22:45
- Marcado como respuesta Eder Costa viernes, 9 de noviembre de 2012 14:09
domingo, 28 de octubre de 2012 19:59 -
Hola.
Emplea una tabla temporal, al menos al principio, ya que puedes indexarla, lo que es un punto esencial. Que la tabla a actualizar se consulte para la obtención de dicha tabla temporal no tiene mayor inconveniente. En resumen, tu tabla temporal tendrá dos campos: el ID y el dato a actualizar (en tu ejemplo le llamaste "Valor"), y tendría una clave primaria (e índice clustered) que sería dicho ID:
create table #tmp (ID int not null primary key clustered, valor varchar(10)); insert #tmp select ID, valor from ... --> Aquí tu subconsulta, con los filtros, etc. update T set Campo = tmp.Valor from Tabla T inner join #tmp tmp on T.ID = #tmp.ID
Si sólo con esto ganas, nos dices. Si sigue siendo una transacción demasiado prolongada, hay cosas que pueden seguir realizándose, como dividir el update en una sucesión de updates en un bucle. De esta manera, por lo menos tendrías muchas transacciones breves en lugar de una muy grande, algo que es muy peligroso como te comentaba.
Ya nos contarás.
Alberto López Grande
SQL Server MVP
Visita mi blog en http://qwalgrande.com
Sígueme en twitter en http://twitter.com/qwalgrande- Propuesto como respuesta Ahias Portillo(FoxBurros)MVP lunes, 29 de octubre de 2012 0:31
- Marcado como respuesta Eder Costa viernes, 9 de noviembre de 2012 14:09
domingo, 28 de octubre de 2012 21:28Moderador
Todas las respuestas
-
Hola Jose,
Cambiar el Recovery Model a SIMPLE no evita que las transacciones sean grabadas en el log. El efecto secundario de que el log no crece sucede porque en SIMPLE Recovery, el log es "truncated" en cada checkpoint. Esta es la razon por la que no puedes hacer backups del log cuando estas ne SIMPLE Recovery.
En FULL Recovery, el log recibe las transacciones, despues de un tiempo las transacciones son grabadas al disco y el log sigue grande hasta que haces un backup del log. Esto garantiza que puedas recuperar tus transacciones hasta cualquier punto en el tiempo restaurando el log.
En SIMPLE Recovery, el log recibe las transacciones, despues de un tiempo son grabadas al disco y el log es "truncated". Como las transacciones son eliminadas del log, ya no tiene sentido hacer backups del log y es asi que este proceso no es posible.
Saludos,
Monica
This posting is provided "AS IS" with no warranties, and confers no rights. Please remember to click "Mark as Answer" and "Vote as Helpful" on posts that help you. This can be beneficial to other community members reading the thread.- Propuesto como respuesta Ahias Portillo(FoxBurros)MVP domingo, 28 de octubre de 2012 22:45
- Marcado como respuesta Eder Costa viernes, 9 de noviembre de 2012 14:09
domingo, 28 de octubre de 2012 19:59 -
Hola.
En cuanto a tu duda, lo que te ha comentado ya Mónica, no hay ninguna relación entre el modelo de recuperación y el rendimiento salvo ciertos matices secundarios y de un impacto muy lateral.
En cuanto a tu sentencia a optimizar, sí parece que haya mucho margen de mejora, por más que sean algo más de un millón los registros a actualizar, una transacción de 27 minutos es muy peligrosa. ¿Puedes compartir la sentencia exacta y el plan de ejecución?
Así a ciegas, yo probaría a preparar una tabla temporal con la subconsulta que contuviera los campos que formen la clave primaria de la tabla a actualizar, filtrada por las condiciones (no expones cuáles), y con el campo "valor" también. El índice clustered de esa tabla temporal sería el mismo que el de la tabla actualizar. Así, tu update se limitaría a algo así:
update T set Campo = tmp.Valor from Tabla T inner join #temporal tmp on T.PK = tmp.PK
Si la subconsulta es cosa de 4 segundos, la creación de la tabla temporal no sería muy costosa y puede que luego aligerara notablemente el update. O al menos tendrías una aproximación diferente que podría reducir el tiempo del update, y estudiando el plan de esta propuesta quizá logres bajar drásticamente los tiempos.
Déjanos saber qué tal te va.
Alberto López Grande
SQL Server MVP
Visita mi blog en http://qwalgrande.com
Sígueme en twitter en http://twitter.com/qwalgrande- Propuesto como respuesta Ahias Portillo(FoxBurros)MVP domingo, 28 de octubre de 2012 22:46
domingo, 28 de octubre de 2012 20:17Moderador -
Uff, pues esa es una noticia terrible. :-( Pensé que en SIMPLE podría evitar el log. Ni modo, entonces procederé a solicitar profiling a mi gente de SQL para determinar a ciencia cierta la procedencia de estos 27 minutos y que me digan si definitivamente no se puede hacer nada al respecto.
Jose R. MCP
Code Samplesdomingo, 28 de octubre de 2012 20:22 -
Hola Alberto. Me llama la atención eso de grabar la subconsulta en una tabla temporal. La verdad es algo que no se me había ocurrido. Pregunta rápida relacionada: ¿Es más eficiente una tabla temporal o una variable de tabla?
Desafortunadamente no puedo compartir la consulta completa o los detalles de las tablas pues el cliente es muy reservado. Puedo decirle que el plan de ejecución original hacía un CLUSTERED INDEX SCAN sobre la tabla y con mi optimización (un covering index) ahora hace un INDEX SEEK y pasó de 26 a 4 segundos. Sí le puedo contar que la subconsulta utiliza la tabla que es objeto del UPDATE. No sé si por ahí haya algún atraso de algún tipo por tener que leer y escribir a la misma tabla.
El plan de ejecución actual estima la actualización del clustered index en un 71% (si mal no recuerdo), siendo la operación a optimizar, pero la verdad no sé cómo se optimiza dicha operación. Creo que no hay forma, ¿cierto?
El CLUSTERED INDEX NO ES EL PRIMARY KEY. La tabla usa un IDENTITY bigint como clave primaria, pero el uso primario de la tabla exige otro índice: Usuario, periodo y servicio. Este índice es el agrupado (clustered). En total hay 3 índices ahora: El agrupado (usuario, periodo y servicio), el de clave primaria (ID) y el nuevo (Periodo que incluye usuario, servicio y consumo).
Hoy no puedo probar la consulta usando tabla temporal. Sería hasta mañana (unas 18 horas).
Jose R. MCP
Code Samplesdomingo, 28 de octubre de 2012 20:34 -
Pero evitar el log en una transaccion tan grande seria un riesgo muy alto. Si la transaccion fuera interrumpida por cualquier motivo terminarias con tu base de datos en estado inestable. Este proceso sucede de esa forma para garantizar la integridad de los datos, garantia que es mucho mas necesaria si estas haciendo updates a millones de registros en una sola transaccion.
This posting is provided "AS IS" with no warranties, and confers no rights. Please remember to click "Mark as Answer" and "Vote as Helpful" on posts that help you. This can be beneficial to other community members reading the thread.domingo, 28 de octubre de 2012 20:38 -
Hola.
Emplea una tabla temporal, al menos al principio, ya que puedes indexarla, lo que es un punto esencial. Que la tabla a actualizar se consulte para la obtención de dicha tabla temporal no tiene mayor inconveniente. En resumen, tu tabla temporal tendrá dos campos: el ID y el dato a actualizar (en tu ejemplo le llamaste "Valor"), y tendría una clave primaria (e índice clustered) que sería dicho ID:
create table #tmp (ID int not null primary key clustered, valor varchar(10)); insert #tmp select ID, valor from ... --> Aquí tu subconsulta, con los filtros, etc. update T set Campo = tmp.Valor from Tabla T inner join #tmp tmp on T.ID = #tmp.ID
Si sólo con esto ganas, nos dices. Si sigue siendo una transacción demasiado prolongada, hay cosas que pueden seguir realizándose, como dividir el update en una sucesión de updates en un bucle. De esta manera, por lo menos tendrías muchas transacciones breves en lugar de una muy grande, algo que es muy peligroso como te comentaba.
Ya nos contarás.
Alberto López Grande
SQL Server MVP
Visita mi blog en http://qwalgrande.com
Sígueme en twitter en http://twitter.com/qwalgrande- Propuesto como respuesta Ahias Portillo(FoxBurros)MVP lunes, 29 de octubre de 2012 0:31
- Marcado como respuesta Eder Costa viernes, 9 de noviembre de 2012 14:09
domingo, 28 de octubre de 2012 21:28Moderador