Leer en inglés

Compartir a través de

优化行组质量以提升列存储索引性能

行组质量取决于行组中的行数。 增加可用内存可以使列存储索引压缩到每个行组中的行数最大化。 使用这些方法可提高列存储索引的压缩率和查询性能。

行组大小为何重要

由于列存储索引通过扫描各个行组的列段来扫描表,最大程度地提高每个行组中的行数可以提高查询性能。 当行组具有大量行时,数据压缩会改进,这意味着从磁盘读取的数据较少。

有关行组的详细信息,请参阅 列存储索引指南

行组的目标大小

为了获得最佳查询性能,目标是最大化列存储索引中每行组的行数。 行组最多可以有 1,048,576 行。 每个行组不一定要具有最大行数。 当行组至少有 100,000 行时,列存储索引的性能良好。

行组可以在压缩期间进行剪裁

在大容量加载或列存储索引重新生成期间,有时没有足够的内存来压缩为每个行组指定的所有行。 出现内存压力时,列存储索引会调整行组的大小,以确保压缩至列存储可以成功。

如果内存不足,无法将至少 10,000 行压缩到每个行组中,将生成错误。

有关批量加载的详细信息,请参阅 Bulk load into a clustered columnstore index(批量加载到聚集列存储索引中)。

如何监视行组质量

动态管理视图(DMV)(sys.dm_db_column_store_row_group_physical_stats 包含与 SQL DB 匹配的视图定义),该视图公开有用的信息,例如行组中的行数,以及剪裁的原因(如果有剪裁)。 可创建下列视图来轻松查询此 DMV,以便获得关于行组修整的信息。

CREATE VIEW dbo.vCS_rg_physical_stats
AS
WITH cte
AS
(
select   tb.[name]                    AS [logical_table_name]
,        rg.[row_group_id]            AS [row_group_id]
,        rg.[state]                   AS [state]
,        rg.[state_desc]              AS [state_desc]
,        rg.[total_rows]              AS [total_rows]
,        rg.[trim_reason_desc]        AS trim_reason_desc
,        mp.[physical_name]           AS physical_name
FROM    sys.[schemas] sm
JOIN    sys.[tables] tb               ON  sm.[schema_id]          = tb.[schema_id]
JOIN    sys.[pdw_table_mappings] mp   ON  tb.[object_id]          = mp.[object_id]
JOIN    sys.[pdw_nodes_tables] nt     ON  nt.[name]               = mp.[physical_name]
JOIN    sys.[dm_pdw_nodes_db_column_store_row_group_physical_stats] rg      ON  rg.[object_id]     = nt.[object_id]
                                                                            AND rg.[pdw_node_id]   = nt.[pdw_node_id]
                                        AND rg.[distribution_id]    = nt.[distribution_id]
)
SELECT *
FROM cte;

trim_reason_desc 列指示行组是否已剪裁(trim_reason_desc = NO_TRIM表示没有剪裁,行组质量最佳)。 下列修整原因指示行组的过早修整:

  • BULKLOAD:当加载的行的传入批小于 100 万行时,使用此修整原因。 如果插入的行数(而不是插入增量存储)大于 100,000,引擎将创建压缩的行组,但会将修整原因设置为 BULKLOAD。 在此方案中,请考虑增加批处理负载以包含更多行。 此外,重新评估分区方案,以确保它不过于精细,因为行组不能跨越分区边界。
  • MEMORY_LIMITATION:若要创建包含 100 万行的行组,引擎需要一定数量的工作内存。 当加载会话中的可用内存小于所需的工作内存时,行组会被提前修剪。 以下部分介绍如何估计所需的内存并分配更多内存。
  • DICTIONARY_SIZE:此修整原因指示,由于至少有一个字符串列具有宽和/或高基数字符串,因此发生行组修整。 字典大小在内存中限制为 16 MB,达到此限制后,将压缩行组。 如果确实遇到这种情况,请考虑将有问题的列隔离到单独的表中。

如何估算内存要求

压缩一个行组所需的最大内存大致如下:

  • 72 MB +
  • #rows * #columns * 8 字节 +
  • #rows * #short-string-columns * 32 字节 +
  • #long-string-columns * 16 MB 用于压缩字典

Nota

短字符串列使用字符串数据类型 <= 32 个字节,而长字符串列使用 32 个字节的 > 字符串数据类型。

长字符串使用用于压缩文本的压缩方法进行压缩。 此压缩方法使用 字典 来存储文本模式。 字典的最大大小为 16 MB。 行组中每个长字符串列只能有一个词典。

减少内存要求的方法

使用以下技术来减少将行组压缩为列存储索引的内存要求。

减少所用列数

如果可能,请设计包含更少列的表。 将行组压缩到列存储中时,列存储索引会单独压缩每个列段。 因此,压缩行组的内存要求随着列数的增加而增加。

使用较少的字符串列

字符串数据类型的列需要比数值和日期数据类型更多的内存。 若要降低内存需求,请考虑从事实数据表中删除字符串列,并将其放入较小的维度表中。

字符串压缩的其他内存要求:

  • 最多 32 个字符的字符串数据类型可能需要每个值 32 个额外的字节。
  • 使用字典方法压缩超过 32 个字符的字符串数据类型。 行组中的每列最多可能需要 16 MB 才能生成字典。

避免过度分区

列存储索引为每个分区创建一个或多个行组。 对于 Azure Synapse Analytics 中的数据仓库,分区数会很快增长,因为数据已分布,并且每个分布区都已分区。 如果表的分区过多,则可能没有足够的行来填充行组。 缺少行不会在压缩期间产生内存压力,但会导致行组无法达到最佳列存储查询性能。

避免过度分区的另一个原因是,将行加载到分区表的列存储索引会导致内存开销。 在加载期间,许多分区可能会接收传入的数据行,这些行保存在内存中,直到每个分区积累到足够的行可以进行压缩处理。 分区过多会产生额外的内存压力。

简化加载查询

数据库在查询中的所有运算符之间共享查询的内存授予。 当负载查询具有复杂的排序和联接时,可用于压缩的内存会减少。

请仅针对加载查询而设计加载查询。 如果需要对数据运行转换,请将它们与加载查询分开运行。 例如,将数据暂存到堆表中,运行转换,然后将暂存表加载到列存储索引中。

调整 MAXDOP

当每次分发有多个 CPU 内核可用时,每次分发都会将行组并行压缩到列存储中。 并行度需要额外的内存资源,这可能会造成内存压力和行组修剪。

若要降低内存压力,可以使用 MAXDOP 查询提示来强制负载作在每个分发中以串行模式运行。

CREATE TABLE MyFactSalesQuota
WITH (DISTRIBUTION = ROUND_ROBIN)
AS SELECT * FROM FactSalesQuota
OPTION (MAXDOP 1);

分配更多内存的方法

DWU 大小和用户资源类共同确定用户查询可用的内存量。 若要增加负载查询的内存授予,可以增加 DWU 数或增加资源类。

后续步骤

若要查找在 Synapse SQL 中提高性能的更多方法,请参阅 性能概述