Es bien conocido por todos que nos arriesgamos a graves problemas de rendimiento en SQL Server si no mantenemos las estadísticas razonablemente actualizadas. Las estadísticas e histogramas son una fuente de información vital para que el optimizador basado en coste realice su labor de forma adecuada. El uso de un índice, de un método de acceso u otro a una tabla quedan determinados, por los datos estadísticos de los que se disponga.

Una vez que conocemos lo importante que es mantener dichas estadísticas actualizadas, debemos determinar la mejor estrategia para conseguirlo minimizando el impacto en nuestro sistema. No olvidemos que actualizar estadísticas, especialmente en tablas grandes, es una tarea que tiene un consumo de recursos considerable. En algunos escenarios es suficiente con delegar esta actualización de las estadísticas a SQL Server (habilitando la auto creación y actualización de estadísticas) y en otros podemos preferir hacerlo de forma manual para tener un mayor control. En la mayoría de los casos el permitir a SQL Server realizar este mantenimiento automáticamente será más que suficiente pero no por ello podemos confiar ciegamente en ello. Es por ello que habitualmente recomendamos, adicionalmente, incluir en nuestro plan de mantenimiento un proceso de actualización de estadísticas.

Con SQL Server 2005 tenemos una nueva funcionalidad de actualización de estadísticas asíncrona. ¿Qué nos aporta la actualización asíncrona? Básicamente una mejor estabilidad en el tiempo de respuesta ante consultas de nuestro servidor. Para comprobar que estamos en lo cierto, nada mejor que un ejemplo J

Partiendo de un escenario con actualización síncrona y automática de estadísticas (configuración por defecto en SQL Server 2005) vamos a analizar el tiempo de respuesta de una misma consulta ejecutada N veces mientras se producen actualizaciones de estadísticas. Para conseguir que se actualicen las estadísticas, provocaremos algunos cambios significativos (que acumulen >20% filas) sobre una tabla mientras realizamos consultas sobre dichos datos.

— Creamos una copia de order details vacía

select * into orderscopy from northwind.dbo.[Order Details]

where 1=2;

— Añadimos un índice

create index iOrdersCopy on orderscopy(OrderId,ProductID);

— Añadimos filas y vamos consultando en un bucle

while (1=1)

begin

    insert into orderscopy select * from northwind.dbo.[Order Details]

    select * from orderscopy where orderid=10253 and ProductID > 39

end

 

El script se limita a, dentro de un bucle infinito, insertar filas y realizar una consulta bastante selectiva sobre el conjunto de éstas. A continuación ejecutaremos, con la configuración por defecto al crear una base de datos, el script y mediremos los tiempos de ejecución (en us) de las inserciones y las consultas.

 

A continuación modificaremos la base de datos para que utilice actualizaciones asíncronas de estadísticas y vaciaremos la tabla orderscopy antes de lanzar el script de nuevo. Para cambiar a modo asíncrono ejecutaremos el siguiente ALTER:

alter database stats

set AUTO_UPDATE_STATISTICS_ASYNC ON

Y estos son los resultados en modo asíncrono:

Parece ser que no estábamos tan en lo cierto (o eso parece vistos estos resultados). Como podemos ver, no existen grandes diferencias entre ambas alternativas (síncrona vs asíncrona) y en ambas se obtiene un patrón similar. Esto es lógico pues depende del porcentaje de cambios que provocan la actualización de estadísticas y esto solo depende del número de ejecuciones del bucle. Algo se nos está escapando por lo que, traza de profiler en mano, procedemos a analizar una actualización síncrona y una asíncrona:

Actualización síncrona

Como podemos ver, la ejecución del SELECT paga el precio de la actualización síncrona de las estadísticas. Un batch que únicamente necesita unos escasos ms para la consulta acaba necesitando más de medio segundo para su ejecución. Además, siempre tendremos la duda de cuanta mejora aporta realmente el plan «más optimizado» que generará el optimizador gracias a dichas estadísticas actualizadas. En resumen, estamos pagando un alto precio de forma síncronamente a la consulta.

Actualización asíncrona

En el caso de la actualización asíncrona vemos que la ejecución de la SELECT únicamente «anota» la operación pendiente de realizar. Esto hace que el batch completo (anotación + SELECT) no llegue a los 20ms. Mientras tanto, de forma asíncrona, se realiza la actualización de estadísticas la cual acaba necesitando casi de 2 segundos. Debemos tener en cuenta que en este caso la tabla de la que tenemos que generar estadísticas es relativamente pequeña (3768 KB).

¿Qué alternativa es la mejor? La respuesta, como casi siempre, es «DEPENDE» J

La opción por defecto (actualizaciones síncronas) nos permite tener una mayor garantía de que no tendremos planes de ejecución «no optimos» debido a unas estadísticas que no están actualizadas a cambio de arriesgarnos a pagar un alto precio si actualizar las estadísticas lleva más tiempo del previsto. Un ejemplo típico es un sistema OLTP donde la duración de las transacciones es crítica y debe ser lo más corta posible. En estos sistemas habitualmente se realizan muchas transacciones de forma simultánea, afectando a un número reducido de datos pero sobre un conjunto de éstos bastante grande. En este escenario no suelen ser habituales las actualizaciones de estadísticas pero podrían darse si otros procesos externos realizan operaciones con cambios masivos.

Las actualizaciones asíncronas nos libran de la atadura de tener que actualizar las estadísticas «en ese momento» a cambio de poder tener planes subóptimos en algunas ocasiones. En sistemas donde se realicen modificaciones importantes sobre tablas e, inmediatamente, se lancen consultas sobre dichas tablas este comportamiento puede ser muy indeseable. Esto nos lleva a pensar que probablemente no sería recomendable tener actualizaciones asíncronas en sistemas con predominio de procesos batch, importaciones de datos y recálculos masivos, con mucho uso de tablas temporales de un solo uso, etc.

En conclusión creo que en aquellos escenarios donde no tenemos un control total sobre las operaciones que se realizan en la base de datos y no podemos hilar siempre muy fino es preferible tener actualizaciones síncronas. No obtendremos la mejor de las latencias y posiblemente suframos de algunos «parones» en algunas consultas pero minimizamos el riesgo que unas pocas consultas OLTP «optimizadas» en base a unas estadísticas no adecuadas nos den un gran disgusto. El uso de actualizaciones asíncronas lo reservaríamos para aquellas bases de datos donde tengamos necesidades de baja latencia aunque tengamos que «trabajarnos» un poco más los procesos batch/masivos. Si tenemos el control total de las operaciones (por ejemplo con una API de procedimientos almacenados), nada nos impide incluir los «sp_updatestats/update statistics» que consideremos apropiados antes de cualquier consulta que, tras analizarla, es demasiado arriesgado ejecutarla sin unas buenas estadísticas. Seguramente más de uno pueda pensar que esto sería «matar moscas a cañonazos» pero no debemos olvidar que la actualización de estadísticas en SQL Server 2005 es suficientemente inteligente como para no llevar a cabo la actualización de estadísticas si los datos no han cambiado lo suficiente.

 

Rubén Garrigós