No estoy seguro de si es un error, per se, pero definitivamente es un hecho interesante. Las reconstrucciones de particiones en línea son nuevas en SQL Server 2014, por lo que puede haber algunas partes internas para resolver esto.
Aquí está mi mejor explicación para ti. Las estadísticas incrementales requieren que todas las particiones se muestreen a la misma velocidad, de modo que cuando el motor fusiona las páginas de estadísticas, puede estar seguro de que la distribución muestreada es comparable. REBUILD
necesariamente muestrea datos a una frecuencia de muestreo del 100%. No hay garantía de que la frecuencia de muestreo del 100% en la partición 9 siempre sea la frecuencia de muestreo exacta del resto de las particiones. Debido a esto, parece que el motor no puede fusionar las muestras y terminas con un blob de estadísticas vacío. Sin embargo, el objeto de estadísticas sigue ahí:
select
check_time = sysdatetime(),
schema_name = sh.name,
table_name = t.name,
stat_name = s.name,
index_name = i.name,
stats_column = index_col(quotename(sh.name)+'.'+quotename(t.name),s.stats_id,1),
s.stats_id,
s.has_filter,
s.is_incremental,
s.auto_created,
sp.last_updated,
sp.rows,
sp.rows_sampled,
sp.unfiltered_rows,
modification_counter
from sys.stats s
join sys.tables t
on s.object_id = t.object_id
join sys.schemas sh
on t.schema_id = sh.schema_id
left join sys.indexes i
on s.object_id = i.object_id
and s.name = i.name
outer apply sys.dm_db_stats_properties(s.object_id, s.stats_id) sp
where t.name = 'TransactionHistory' and sh.name = 'dbo'
Puede llenar el blob a través de cualquier cantidad de medios:
UPDATE STATISTICS dbo.TransactionHistory (IDX_ProductId) WITH RESAMPLE;
o
UPDATE STATISTICS dbo.TransactionHistory (IDX_ProductId) WITH RESAMPLE ON PARTITIONS (9);
o puede esperar a que AutoStats se actualice en la primera compilación de un plan de consulta con ese objeto:
-- look at my creative query
select *
from dbo.TransactionHistory
where TransactionDate = '20140101';
Habiendo dicho todo eso, esta publicación esclarecedora de Erin Stellato destaca lo que se percibe como una deficiencia importante de las estadísticas incrementales. El optimizador no utiliza sus datos a nivel de partición en la generación del plan de consulta, lo que reduce el supuesto beneficio de las estadísticas incrementales. ¿Cuál es, entonces, el beneficio actual de las estadísticas incrementales? Diría que su utilidad principal es la capacidad de muestrear tablas grandes de manera más consistente a una tasa más alta que con las estadísticas tradicionales.
Usando su ejemplo, así es como se ven las cosas:
set statistics time on;
update statistics dbo.TransactionHistory(IDX_ProductId)
with fullscan;
--SQL Server Execution Times:
-- CPU time = 94 ms, elapsed time = 131 ms.
update statistics dbo.TransactionHistory(IDX_ProductId)
with resample on partitions(2);
--SQL Server Execution Times:
-- CPU time = 0 ms, elapsed time = 5 ms.
drop index IDX_ProductId On dbo.TransactionHistory;
CREATE NONCLUSTERED INDEX IDX_ProductId ON dbo.TransactionHistory (ProductId)
WITH (DATA_COMPRESSION = ROW)
ON [PRIMARY]
update statistics dbo.TransactionHistory(IDX_ProductId)
with fullscan;
--SQL Server Execution Times:
-- CPU time = 76 ms, elapsed time = 66 ms.
Una actualización de estadísticas de exploración completa sobre los costos estadísticos incrementales de 131 ms. Una actualización de estadísticas de exploración completa en la estadística sin alineación de partición cuesta 66 ms. La estadística no alineada es más lenta probablemente debido a la sobrecarga incurrida al fusionar las páginas de estadísticas individuales nuevamente en el histograma principal. Sin embargo, usando el objeto estadístico alineado con la partición, podemos actualizar una partición y fusionarla nuevamente en el blob de histograma principal en 5 ms. Entonces, en este punto, el administrador con la estadística incremental se enfrenta a una decisión. Pueden disminuir el tiempo general de mantenimiento de las estadísticas solo actualizando las particiones que tradicionalmente tendrían que actualizarse, o pueden experimentar con frecuencias de muestreo más altas de modo que potencialmente obtengan más filas muestreadas en el mismo período de tiempo que su período de mantenimiento anterior. El primero permite respirar en la ventana de mantenimiento, el segundo puede llevar las estadísticas en una tabla muy grande a un lugar donde las consultas obtengan mejores planes basados en estadísticas más precisas. Esto no es una garantía y su kilometraje puede variar.
El lector puede ver que 66 ms no es un tiempo de actualización de estadísticas doloroso en esta tabla, así que intenté configurar una prueba en el conjunto de datos de stackexchange. Hay 6.418.608 publicaciones (excluyendo las publicaciones de StackOverflow y todas las publicaciones de 2012, un error de datos por mi parte) en el volcado reciente que descargué.
He particionado los datos por [CreationDate]
... demo.
Aquí hay algunos tiempos para algunos escenarios bastante estándar (100% - reconstrucción de índice, predeterminado - actualización automática de estadísticas o UPDATE STATISTICS
sin una frecuencia de muestreo especificada:
- Crear estadística no incremental con Fullscan: tiempo de CPU = 23500 ms, tiempo transcurrido = 22521 ms.
- Crear estadística incremental con exploración completa: tiempo de CPU = 20406 ms, tiempo transcurrido = 15413 ms.
- Actualizar estadística no incremental con frecuencia de muestreo predeterminada: tiempo de CPU = 406 ms, tiempo transcurrido = 408 ms.
- Actualizar estadística incremental con frecuencia de muestreo predeterminada: tiempo de CPU = 453 ms, tiempo transcurrido = 507 ms.
Supongamos que somos más sofisticados que estos escenarios predeterminados y hemos decidido que una tasa de muestra del 10% es la tasa mínima que debería proporcionarnos los planes que necesitamos y al mismo tiempo mantener el tiempo de mantenimiento en un plazo razonable.
- Actualice la estadística no incremental con un 10 por ciento de muestra: tiempo de CPU = 2344 ms, tiempo transcurrido = 2441 ms.
- Actualice la estadística incremental con una muestra del 10 por ciento: tiempo de CPU = 2344 ms, tiempo transcurrido = 2388 ms.
Hasta ahora no hay un beneficio claro de tener una estadística incremental. Sin embargo, si aprovechamos el DMV indocumentado sys.dm_db_stats_properties_internal()
(a continuación), puede obtener una idea de qué partición (es) desea actualizar. Digamos que hicimos cambios en los datos en la partición 3 y queremos asegurarnos de que las estadísticas estén actualizadas para las consultas entrantes. Aquí están nuestras opciones:
- Actualización no incremental por defecto (también el comportamiento predeterminado de la actualización automática de estadísticas): 408 ms.
- Actualización no incremental al 10%: 2441 ms.
- Actualice las estadísticas incrementales, partición 3 con remuestreo (10% - nuestra frecuencia de muestreo definida): tiempo de CPU = 63 ms, tiempo transcurrido = 63 ms.
Aquí es donde debemos tomar una decisión. ¿Nos llevamos la victoria de un 63 ms. actualización de estadísticas basada en particiones, ¿o aumentamos aún más la frecuencia de muestreo? Digamos que estamos dispuestos a tomar el golpe inicial de muestreo al 50% en una estadística incremental:
- Actualizar estadísticas incrementales al 50%: tiempo transcurrido = 16840 ms.
- Actualizar estadísticas incrementales, partición 3 con remuestreo (50% - nuestro nuevo tiempo de actualización): tiempo transcurrido = 295 ms.
Podemos muestrear muchos más datos, tal vez configurando el optimizador para hacer mejores conjeturas sobre nuestros datos (aunque todavía no está usando estadísticas a nivel de partición) y podemos hacerlo más rápido ahora que tenemos estadísticas incrementales
Sin embargo, una última cosa divertida para resolver. ¿Qué pasa con las actualizaciones de estadísticas sincrónicas? ¿Se conserva la frecuencia de muestreo del 50% incluso cuando se inician los autostats?
Eliminé datos de la partición 3 y ejecuté una consulta en CreationDate y verifiqué y luego verifiqué las tasas con la misma consulta a continuación. La tasa de muestra del 50% se conservó.
Entonces, en resumen: las estadísticas incrementales pueden ser una herramienta útil con la cantidad correcta de pensamiento y el trabajo de configuración inicial. Sin embargo, debe conocer el problema que está tratando de resolver y luego debe resolverlo adecuadamente. Si obtiene estimaciones de cardinalidad incorrectas, es posible que pueda obtener mejores planes con una tasa de muestreo estratégica y alguna intervención invertida. Sin embargo, solo está obteniendo una pequeña porción del beneficio ya que el histograma que se usa es la página de estadísticas combinadas y no la información de nivel de partición. Si siente dolor en su ventana de mantenimiento, entonces quizás las estadísticas incrementales pueden ayudarlo, pero probablemente requerirá que configure un proceso de intervención de mantenimiento de alto contacto. Independientemente,:
- Estadísticas creadas con índices que no están alineados con la partición de la tabla base.
- Estadísticas creadas en bases de datos secundarias legibles AlwaysOn.
- Estadísticas creadas en bases de datos de solo lectura.
- Estadísticas creadas en índices filtrados.
- Estadísticas creadas en vistas.
- Estadísticas creadas en tablas internas.
- Estadísticas creadas con índices espaciales o índices XML.
Espero que esto ayude
select
sysdatetime(),
schema_name = sh.name,
table_name = t.name,
stat_name = s.name,
index_name = i.name,
leading_column = index_col(quotename(sh.name)+'.'+quotename(t.name),s.stats_id,1),
s.stats_id,
parition_number = isnull(sp.partition_number,1),
s.has_filter,
s.is_incremental,
s.auto_created,
sp.last_updated,
sp.rows,
sp.rows_sampled,
sp.unfiltered_rows,
modification_counter = coalesce(sp.modification_counter, n1.modification_counter)
from sys.stats s
join sys.tables t
on s.object_id = t.object_id
join sys.schemas sh
on t.schema_id = sh.schema_id
left join sys.indexes i
on s.object_id = i.object_id
and s.name = i.name
cross apply sys.dm_db_stats_properties_internal(s.object_id, s.stats_id) sp
outer apply sys.dm_db_stats_properties_internal(s.object_id, s.stats_id) n1
where n1.node_id = 1
and (
(is_incremental = 0)
or
(is_incremental = 1 and sp.partition_number is not null)
)
and t.name = 'Posts'
and s.name like 'st_posts%'
order by s.stats_id,isnull(sp.partition_number,1)