¿Importa el orden de las columnas en un índice PK?


33

Tengo algunas tablas muy grandes con la misma estructura básica. Cada uno tiene una RowNumber (bigint)y DataDate (date)columna. Los datos se cargan utilizando SQLBulkImport todas las noches, y nunca se cargan datos "nuevos": es un registro histórico (SQL Standard, no Enterprise, por lo que no se realiza la partición).

Debido a que cada bit de datos debe estar vinculado a otros sistemas, y cada RowNumber/DataDatecombinación es única, esa es mi Clave principal.

Noté que debido a la forma en que definí el PK en el Diseñador de tablas SSMS, RowNumberaparece primero y DataDatesegundo.

También noto que mi fragmentación siempre es MUY alta ~ 99%.

Ahora, debido a que cada uno DataDateaparece solo una vez, esperaría que el indexador solo agregue a las páginas cada día, pero me pregunto si en realidad se está indexando en RowNumberprimer lugar y, por lo tanto, ¿tiene que cambiar todo lo demás?


Rownumberno es una columna de identidad, es un int generado por un sistema externo (lamentablemente). Se restablece al comienzo de cada uno DataDate.

Datos de ejemplo

RowNumber | DataDate | a | b | c..... 
   1      |2013-08-01| x | y | z 
   2      |2013-08-01| x | y | z 
...
   1      |2013-08-02| x | y | z 
   2      |2013-08-02| x | y | z 
...

Los datos se cargan en RowNumberorden, uno DataDatepor carga.

El proceso de importación es bcp: he intentado cargar en una tabla temporal y luego seleccionar en orden desde allí ( ORDER BY RowNumber, DataDate), pero todavía sale una gran fragmentación.

Respuestas:


50

¿Importa el orden de las columnas en un índice PK?

Si lo hace

De manera predeterminada, la restricción de clave principal se aplica en SQL Server mediante un índice agrupado único. El índice agrupado define el orden lógico de las filas en la tabla. Puede haber una cantidad de páginas de índice adicionales agregadas para representar los niveles superiores del índice b-tree, pero el nivel más bajo (hoja) de un índice agrupado es simplemente el orden lógico de los datos en sí.

Para que quede claro, las filas de una página no están necesariamente almacenadas físicamente en orden de clave de índice agrupado. Hay una estructura de indirección separada dentro de la página que almacena un puntero a cada fila. Esta estructura está ordenada por las claves de índice agrupadas. Además, cada página tiene un puntero a la página anterior y siguiente en el mismo nivel en orden de clave de índice agrupado.

Con una clave primaria agrupada de (RowNumber, DataDate), las filas se ordenan lógicamente primero por RowNumbery luego por DataDate, de modo que todas las filas donde RowNumber = 1se agrupan lógicamente, luego las filas donde RowNumber = 2y así sucesivamente.

Cuando agrega nuevos datos ( RowNumbersde 1 a n), las nuevas filas pertenecen lógicamente dentro de las páginas existentes, por lo que SQL Server probablemente tendrá que hacer mucho trabajo dividiendo las páginas para hacer espacio. Toda esta actividad genera mucho trabajo adicional (incluido el registro de los cambios) sin ganancia.

Las páginas divididas también comienzan aproximadamente un 50% vacías, por lo que la división excesiva puede dar como resultado una baja densidad de páginas (menos filas que las óptimas por página). Esto no solo es una mala noticia para leer desde el disco (menor densidad = más páginas para leer), sino que las páginas de menor densidad también ocupan más espacio en la memoria cuando se almacenan en caché.

Cambiar el índice agrupado a (DataDate, RowNumber) significa que los datos nuevos (con, presumiblemente, más altos DataDatesque los almacenados actualmente) se agregan al final lógico del índice agrupado en páginas nuevas. Esto eliminará los gastos generales innecesarios de las páginas divididas y dará como resultado tiempos de carga más rápidos. Los datos menos fragmentados también significan que la actividad de lectura anticipada (leer páginas del disco justo antes de que sean necesarias para una consulta en curso) puede ser más eficiente.

Por lo menos, las consultas son mucho más propensos a buscar en DataDateque RowNumber. Un índice agrupado en (DataDate, RowNumber) admite búsquedas de índice en DataDate(y luego RowNumber). La disposición existente solo admite búsquedas en RowNumber(y solo entonces, tal vez, en DataDate). Es posible que pueda soltar el índice no agrupado existente DataDateuna vez que se cambie la clave primaria. El índice agrupado será más ancho que el índice no agrupado al que reemplaza, por lo que debe realizar una prueba para asegurarse de que el rendimiento siga siendo aceptable.

Al importar datos nuevos con bcp, puede obtener un mayor rendimiento si los datos dentro del archivo de importación se ordenan por las claves de índice agrupadas (idealmente (DataDate, RowNumber) y especifica la bcpopción:

-h "ORDER(DataDate,RowNumber), TABLOCK"

Para obtener el mejor rendimiento de carga de datos, puede intentar lograr inserciones mínimamente registradas. Para más información, ver:


44
Una respuesta excelente: ahora sé QUÉ debo hacer Y por qué. ¡Lo había pensado, pero no lo SABÍA! Gracias.
BlueChippy

Tomó MUCHO MUCHO tiempo para obtener la base de datos en mi servidor SQL local para probar: Antes de alterar la carga del índice, tomó 45 minutos ... ¡¡¡¡¡¡¡¡¡¡¡¡solo 5 !!
BlueChippy

13

Sí, el orden es crítico. Dudo mucho que alguna vez consultes por RowNumber (por ejemplo WHERE RowNumber=1). De manera abrumadora, las series de tiempo se consultan por date ( WHERE DataDate BEWEEN @start AND @end) y tales consultas requerirían una organización agrupada por DataDate.

La fragmentación en general es un arenque rojo. La reducción de la fragmentación no debería ser su objetivo aquí, pero debería tener una organización adecuada para sus consultas. Además, es bueno tener una fragmentación reducida, pero no es un objetivo en sí mismo. Si tiene un modelo de datos correctamente organizado que coincida con su carga de trabajo (sus consultas están cubiertas adecuadamente) y tiene mediciones que muestran que la fragmentación afecta el rendimiento, entonces podemos hablar de ello.


También tengo un índice no agrupado (s) en DataDate, que como usted dice a menudo es una WHEREcláusula en las consultas.
BlueChippy

1
Si ORDER de las columnas es crítico, ¿el impacto del orden incorrecto vería aumentar mi E / S? Mi opinión es que está ordenando por RowNumber y, por lo tanto, tiene que hacer mucho trabajo en los índices cada vez, ¿mientras que debería basarse en DataDate?
BlueChippy
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.