Columna de identidad en el índice del almacén de columnas

9

Tengo una tabla IMO extremadamente grande (~ 137 millones de filas) con muchos datos repetidos, muchas NULLcolumnas y demás.

Estoy considerando explorar esto usando una tabla con COLUMNSTORE INDEXay tengo una IDENTITYcolumna en la tabla original, que es mi única columna donde cada fila es única.

¿Debo dejar esta columna fuera o incluirla? He leído que desea incluir todas las filas de su tabla en la tabla, COLUMNSTORE INDEXpero también he leído que los mejores candidatos son columnas con muchas filas no únicas.

¿Es esto solo un mal candidato para un COLUMNSTORE INDEX?

Estoy usando SQL Server 2012, por lo que es un almacén de columnas no agrupado. Solo estoy explorando posibles mejores formas de almacenar estos datos. Las actualizaciones son inexistentes, aunque periódicamente se agregarán nuevas filas a través de un proceso ELT, por lo que supongo que se realizará algún trabajo allí. Algunas personas extraen estos datos y generan informes enormes, muchas exploraciones de filas, ocasionan que el servidor se rastree a veces, lo que nos ha obligado a descargar una copia diariamente a un servidor secundario.

Don
fuente
1
¿Es la columna de identidad en la tabla original también su índice agrupado? Si es así, SQL Server incluirá automáticamente esa columna en cualquier índice de almacén de columnas no agrupado, incluso si no lo solicita explícitamente. Esto es algo similar a la forma en que las columnas de índice agrupadas se incluirán en un índice de árbol b no agrupado, pero los datos se almacenarán como segmentos de almacenamiento de columnas comprimidos reales en este caso. Consulte dba.stackexchange.com/questions/103722/… para obtener más información.
Geoff Patterson
137 million rowsEs grande pero manejable. ¿Ha buscado particionar la tabla y colocarla en diferentes grupos de archivos? El índice del almacén de columnas en sql 2012 no se puede escribir, por lo que tendrá problemas: debe descartarlo y volver a crearlo. No digo que la voluntad del almacén de columnas sea mala, pero también es mejor explorar otras opciones.
Kin Shah

Respuestas:

11

Las columnas de identidad no están realmente comprimidas en los índices de almacén de columnas en SQL Server 2012 o en SQL Server 2014. Todo dependerá realmente de la carga de trabajo que esté experimentando. Si su carga de trabajo incluirá la columna de identidad, puede aprovechar muy bien la eliminación de segmentos.

Desde el punto de vista de la compresión, Columnstore le proporcionará una mejor compresión que la página normalmente. Típicamente. Por favor, pruébelo antes de avanzar a producción.

Su mayor problema en SQL Server 2012 será una implementación muy débil del modo Batch, y no hay nada que pueda hacer al respecto.

Niko Neugebuer
fuente
77
Bienvenido Niko !!!
Aaron Bertrand
3

No pude resistir unirme a Niko con otra respuesta (¡bienvenido, Niko!). En general, estoy de acuerdo con Niko en que las limitaciones del modo por lotes en SQL 2012 (si Niko no se vincula a su propio blog, lo haré :)) puede ser una preocupación importante. Pero si puede vivir con ellos y tener control total sobre cada consulta que está escribiendo en la tabla para examinarla cuidadosamente, el almacén de columnas podría funcionar para usted en SQL 2012.

En cuanto a sus preguntas específicas sobre la columna de identidad, descubrí que la columna de identidad se comprime muy bien y recomiendo encarecidamente incluirla en su índice de almacén de columnas en cualquier prueba inicial que realice. (Tenga en cuenta que si la columna de identidad también es el índice agrupado de su árbol b, se incluirá automáticamente en su índice de almacén de columnas no agrupado ).

Como referencia, aquí están los tamaños que observé para ~ 10MM filas de datos de columna de identidad. El almacén de columnas cargado para la eliminación óptima del segmento se comprime a 26 MB (frente a 113 MB para la PAGEcompresión de la tabla del almacén de filas), e incluso el almacén de columnas construido en un árbol b ordenado aleatoriamente es de solo 40 MB. Entonces, esto muestra un gran beneficio de compresión, incluso sobre la mejor compresión de b-tree que SQL tiene para ofrecer e incluso si no se molesta en alinear sus datos para una eliminación óptima del segmento (lo que haría primero creando un b-tree y luego construyendo su almacén de columnas con MAXDOP1).

ingrese la descripción de la imagen aquí

Aquí está el script completo que utilicé en caso de que quiera jugar:

-- Confirm SQL version
SELECT @@version
--Microsoft SQL Server 2012 - 11.0.5613.0 (X64) 
--  May  4 2015 19:05:02 
--  Copyright (c) Microsoft Corporation
--  Enterprise Edition: Core-based Licensing (64-bit) on Windows NT 6.3 <X64> (Build 9600: )


-- Create a columnstore table with identity column that is the primary key
-- This will yield 10 columnstore segments @ 1048576 rows each
SELECT i = IDENTITY(int, 1, 1), ROW_NUMBER() OVER (ORDER BY randGuid) as randCol
INTO #testIdentityCompression_sortedColumnstore
FROM (
    SELECT TOP 10485760 ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS randI, NEWID() AS randGuid
    FROM master..spt_values v1
    CROSS JOIN master..spt_values v2
    CROSS JOIN master..spt_values v3
) r
ORDER BY r.randI
GO
ALTER TABLE #testIdentityCompression_sortedColumnstore
ADD PRIMARY KEY (i)
GO
-- Load using a pre-ordered b-tree and one thread for optimal segment elimination
-- See http://www.nikoport.com/2014/04/16/clustered-columnstore-indexes-part-29-data-loading-for-better-segment-elimination/
CREATE NONCLUSTERED COLUMNSTORE INDEX cs_#testIdentityCompression_sortedColumnstore ON #testIdentityCompression_sortedColumnstore (i) WITH (MAXDOP = 1)
GO

-- Create another table with the same data, but randomly ordered
SELECT *
INTO #testIdentityCompression_randomOrderColumnstore
FROM #testIdentityCompression_sortedColumnstore
GO
ALTER TABLE #testIdentityCompression_randomOrderColumnstore
ADD UNIQUE CLUSTERED (randCol)
GO
CREATE NONCLUSTERED COLUMNSTORE INDEX cs_#testIdentityCompression_randomOrderColumnstore ON #testIdentityCompression_randomOrderColumnstore (i) WITH (MAXDOP = 1)
GO

-- Create a b-tree with the identity column data and no compression
-- Note that we copy over only the identity column since we'll be looking at the total size of the b-tree index
-- If anything, this gives an unfair "advantage" to the rowstore-page-compressed version since more
-- rows fit on a page and page compression rates should be better without the "randCol" column.
SELECT i
INTO #testIdentityCompression_uncompressedRowstore
FROM #testIdentityCompression_sortedColumnstore
GO
ALTER TABLE #testIdentityCompression_uncompressedRowstore
ADD PRIMARY KEY (i)
GO

-- Create a b-tree with the identity column and page compression
SELECT i
INTO #testIdentityCompression_compressedRowstore
FROM #testIdentityCompression_sortedColumnstore
GO
ALTER TABLE #testIdentityCompression_compressedRowstore
ADD PRIMARY KEY (i)
WITH (DATA_COMPRESSION = PAGE)
GO

-- Compare all the sizes!
SELECT OBJECT_NAME(p.object_id, 2) AS tableName, COUNT(*) AS num_segments, SUM(on_disk_size / (1024.*1024.)) as size_mb
FROM tempdb.sys.partitions p
JOIN tempdb.sys.column_store_segments s
    ON s.partition_id = p.partition_id
    AND s.column_id = 1
WHERE p.object_id IN (OBJECT_ID('tempdb..#testIdentityCompression_sortedColumnstore'),OBJECT_ID('tempdb..#testIdentityCompression_randomOrderColumnstore'))
GROUP BY p.object_id
UNION ALL
SELECT OBJECT_NAME(p.object_id, 2) AS tableName
    , NULL AS num_segments
    , (a.total_pages*8.0) / (1024.0) as size_mb
FROM tempdb.sys.partitions p
JOIN tempdb.sys.allocation_units a
    ON a.container_id = p.partition_id
WHERE p.object_id IN (OBJECT_ID('tempdb..#testIdentityCompression_compressedRowstore'),OBJECT_ID('tempdb..#testIdentityCompression_uncompressedRowstore'))
ORDER BY 3 ASC
GO
Geoff Patterson
fuente
Gracias por todas las excelentes respuestas, en este momento he decidido esperar hasta que pueda obtener al menos el servidor sql 2014. Estamos intensificando nuestras actualizaciones, así que espero que el próximo año podamos hacer esto.
Don