列存索引最适合大规模数据聚合场景,如group by、sum、count、avg、min、max等操作,尤其当表行数超百万、聚合字段为整型或日期型、且查询需扫描大范围数据时效果最显著;它不适用于点查或高频单行更新。

列存索引适合什么聚合场景
列存索引(Columnstore Index)在 SQL Server 2019 中专为 GROUP BY、SUM、COUNT、AVG、MIN、MAX 等聚合计算优化,尤其当表行数超百万、参与聚合的列是整型或日期型、且查询常扫描大范围数据时效果最明显。它不适合点查(如 WHERE ID = 123)或高频率单行更新——这类操作会触发 deltastore 刷盘和重建开销。
创建聚集列存索引前必须停用非聚集索引
SQL Server 2019 不允许在已有非聚集索引的表上直接创建聚集列存索引(CREATE CLUSTERED COLUMNSTORE INDEX)。否则报错:Msg 35337, Level 16, State 1, Line X: Cannot create a clustered columnstore index on a table that has nonclustered indexes.
实操建议:
- 先用
SELECT name FROM sys.indexes WHERE type = 2 AND object_id = OBJECT_ID('YourTable')检查是否存在非聚集索引 - 批量删除:执行
DROP INDEX [index_name] ON [YourTable],或用脚本生成所有DROP INDEX语句 - 若业务依赖某些非聚集索引用于点查,可改用非聚集列存索引(
CREATE NONCLUSTERED COLUMNSTORE INDEX),但注意它不能作为主键/唯一约束载体
聚合性能受数据压缩率与批处理影响
列存索引的加速能力高度依赖数据压缩效率和向量化执行(Vectorized Execution)。如果 OrderDate 是 datetime2 且值离散度高(如每行都不同),压缩率低,批处理效率下降;而 Status 这类低基数字符串列反而压缩比高、聚合快。
实操建议:
- 避免在列存索引中包含大量
varchar(max)或xml列——它们不进列存结构,只存于 deltastore,拖慢聚合 - 对聚合字段(如
Amount,Quantity)优先使用int/bigint/decimal(18,2),避免float引发精度比较开销 - 确认是否启用向量化:在执行计划中查找
Batch Hash Join或Batch Sort算子;若没出现,检查是否因统计信息过旧或内存不足被降级
混合使用行存 + 列存索引需警惕维护冲突
SQL Server 2019 支持在一个表上同时存在聚集行存索引(如主键)和非聚集列存索引,但写入路径会分裂:INSERT/UPDATE 走行存路径,而列存索引需异步刷新(通过 tuple mover 后台任务)。这会导致实时性偏差——刚插入的数据可能在列存索引中不可见,直到 deltastore 达到 102400 行或 5 分钟超时后合并。
容易踩的坑:
- 报表类查询若依赖“最新小时数据”,不要直接查非聚集列存索引,应加
WITH (NOLOCK)并联合查行存索引补漏,或改用ALTER INDEX ... REORGANIZE WITH (COMPRESS_ALL_ROW_GROUPS = ON)强制刷出 - 频繁小批量 INSERT(如每秒几十条)会让 deltastore 长期处于 open 状态,
sys.dm_db_column_store_row_group_physical_stats中state_desc显示OPEN,此时聚合结果不准 - UPDATE/DELETE 会标记行存索引中的行,但列存索引不感知——除非走
DELETE FROM ... WHERE触发列存段级删除,否则逻辑删除仅在行存层生效
Columnstore Index Scan,不代表实际用了向量化批处理——得看是否真出现了 Actual Number of Rows Read 和 Estimated Number of Rows 接近,以及 CPU 时间是否显著低于逻辑读。










