Cuando una tabla tiene un índice agrupado, el índice son los datos de la tabla (de lo contrario, tiene una tabla de tipo de montón). Una reconstrucción del índice agrupado (cualquier índice de hecho, pero el espacio no se contará como "datos" para un índice no agrupado) dará como resultado que las páginas parcialmente utilizadas se fusionen en una forma más completa.
A medida que inserta datos en un índice (agrupado o no) en el orden del índice, las páginas de hojas se crean según sea necesario y solo tendrá una página parcial: la que está al final. A medida que ingresa datos fuera del orden del índice, se debe dividir una página para que los datos se ajusten en el lugar correcto: termina con dos páginas que están aproximadamente a la mitad y la nueva fila entra en una de ellas. Con el tiempo, esto puede suceder mucho, consumiendo una buena cantidad de espacio extra, aunque hasta cierto punto las futuras inserciones llenarán algunos de los vacíos. Las páginas que no son hojas también verán un efecto similar, pero las páginas de datos reales son mucho más significativas en tamaño de lo que lo son.
También elimina puede dar lugar a páginas parciales. Si elimina todas las filas de una página, se cuenta como "sin usar", pero si le quedan una o más filas de datos, todavía se cuenta como en uso. Incluso si solo hay una fila con 10 bytes en una página, esa página cuenta como 8192 bytes en el recuento de espacio utilizado. Nuevamente, las futuras inserciones podrían llenar parte del vacío.
Para las filas de longitud variable, las actualizaciones también pueden tener el mismo efecto: a medida que una fila se hace más pequeña, puede dejar espacio en su página que luego no es fácil de reutilizar, y si una fila en una página casi completa crece más, podría forzar una división de la página .
SQL Server no pasa tiempo tratando de normalizar los datos reorganizando cómo se usan las páginas, hasta que se le indique explícitamente, como su orden de reconstrucción de índice, ya que tales ejercicios de recolección de basura podrían ser una pesadilla de rendimiento.
Sospecho que esto es lo que está viendo, aunque diría que tener suficiente espacio asignado para ~ 2.7 veces la cantidad que los datos necesitan absolutamente es un caso particularmente malo. Puede implicar que tiene algo aleatorio como una de las claves significativas en el índice (una columna UUID tal vez), lo que significa que es poco probable que se agreguen nuevas filas en el orden del índice, y / o que haya ocurrido un número significativo de eliminaciones recientemente.
Ejemplo de división de página
Inserción en orden de índice con filas de longitud fija de las cuales cuatro caben en una página:
Start with one empty page:
[__|__|__|__]
Add the first item in index order:
[00|__|__|__]
Add the next three
[00|02|04|06]
Adding the next will result in a new page:
[00|02|04|06] [08|__|__|__]
And so on...
[00|02|04|06] [08|10|12|14] [16|18|__|__]
Ahora, para agregar filas fuera de orden de índice (es por eso que usé números pares solo arriba): Agregar 11
significaría extender esa segunda página (no es posible ya que son de tamaño fijo), moviendo todo lo anterior arriba de 11 (demasiado caro en un índice grande) o dividir la página así:
[00|02|04|06] [08|10|11|__] [12|14|__|__] [16|18|__|__]
A partir de aquí, agregar 13
y 17
no resultará en una división ya que actualmente hay espacio en las páginas relevantes:
[00|02|04|06] [08|10|11|__] [12|13|14|__] [16|17|18|__]
pero agregar 03 hará lo siguiente:
[00|02|03|__] [04|06|__|__] [08|10|11|__] [12|13|14|__] [16|17|18|__]
Como puede ver, después de esas operaciones de inserción, actualmente tenemos 5 páginas de datos asignadas que podrían caber en un total de 20 filas, pero solo tenemos 14 filas allí ("desperdiciando" el 30% del espacio).
Una reconstrucción con opciones predeterminadas (ver más abajo sobre "factor de relleno") daría como resultado:
[00|02|03|04] [06|08|10|11] [12|13|14|16] [17|18|__|__]
guardando una página en este simple ejemplo. Es fácil ver cómo las eliminaciones pueden tener un efecto similar al de las inserciones fuera de índice.
Mitigación
Si espera que los datos se presenten en un orden bastante aleatorio con respecto al orden del índice, puede usar la FILLFACTOR
opción al crear o reconstruir un índice para indicarle a SQL Server que deje huecos artificialmente para rellenarlos más tarde, reduciendo las divisiones de página a largo plazo, pero tomando más espacio inicialmente. Por supuesto, equivocarse en este valor puede empeorar las cosas en lugar de mejorar la situación, así que maneje con cuidado.
La división de páginas, particularmente en el índice agrupado, puede tener una implicación de rendimiento para las inserciones / actualizaciones, por FILLFACTOR
lo que a veces se modifica por esa razón en lugar del problema del uso del espacio en las bases de datos que ven mucha actividad de escritura (pero para la mayoría de las aplicaciones, donde las lecturas superan a las escrituras en varios órdenes de magnitud, generalmente es mejor dejar el factor de relleno al 100%, excepto en casos específicos como donde tiene índices sobre columnas con contenido efectivamente aleatorio).
Supongo que otros DB de gran nombre tienen una opción similar, si también necesita este nivel de control en ellos.
Actualizar
Con respecto a la ALTER INDEX
declaración agregada a la pregunta después de que comencé a escribir lo anterior: supongo que las opciones son las mismas que cuando se creó el índice por primera vez (o se reconstruyó por última vez), pero si no, la opción de compresión podría ser muy significativa si se agregara esto tiempo alrededor. También en esa declaración, el factor de relleno se establece en 85%, no en 100%, por lo que cada página hoja estará vacía ~ 15% inmediatamente después de la reconstrucción.