字典编码

云原生数据仓库AnalyticDB PostgreSQL版Beam存储引擎提供的字典编码功能,可以将低基数的字符串类型数据压缩为整型数据,提升存储效率,加速过滤、聚合等类型查询的性能。

注意事项

  • 仅存储弹性模式v7.0.x版本支持Beam字典编码功能。

  • Beam字典编码功能在存储弹性模式v7.0.6.2版本中结束公测正式上线,该版本修复了公测中的问题,建议您尽快升级到v7.0.6.2及以上版本。

字典编码存储压缩

使用数据压缩功能可以节约表的存储空间,并减少查询时从磁盘读取的数据量大小,进而减少I/O以提升查询性能。针对不同类型的数据,合理地选择压缩算法可以更好的帮助您提升数据库的性能。

AnalyticDB PostgreSQL版字典编码压缩仅支持字符串类型。字典编码既支持列级别压缩,也支持表级别压缩。

  • 列级别使用字典编码压缩时,仅对指定列进行压缩,且需要保证列的数据类型为字符串类型。

  • 表级别使用字典编码压缩时,满足低基数要求的列会被压缩。针对非字符串类型的列,建表时不进行压缩。

本文以TPC-H的nation表为例,在使用Beam存储表的情况下,通过compresstype='gdict'指定表级别使用字典编码压缩,n_namen_comment两列都会用字典编码进行压缩,示例如下。

CREATE TABLE NATION (
    n_nationkey integer NOT NULL,
    n_name character(25) NOT NULL,
    n_regionkey integer NOT NULL,
    n_comment character varying(152)
)
USING beam WITH (compresstype = 'gdict') 
DISTRIBUTED by (n_nationkey);

您也可以只针对指定列n_name使用字典编码,示例如下。

CREATE TABLE NATION (
    n_nationkey integer NOT NULL,
    n_name character(25) NOT NULL ENCODING (compresstype='gdict'),
    n_regionkey integer NOT NULL,
    n_comment character varying(152)
)
USING beam WITH (compresstype = 'lz4', compresslevel = 9) 
DISTRIBUTE by (n_nationkey);

在对n_name列使用压缩编码后,存储的数据大小从25 Byte(character(25)类型对字符串末尾补齐空格)压缩至2 Byte,显著减少了数据使用的存储空间。

n_name

字符串值

大小(Byte)

字典值

压缩编码大小(Byte)

ALGERIA

25

0

2

ARGENTINA

25

1

2

BRAZIL

25

2

2

CANADA

25

3

2

CHINA

25

4

2

EGYPT

25

5

2

ETHIOPIA

25

6

2

FRANCE

25

7

2

总计

200

/

16

字典编码执行加速

使用字典编码存储后,可以进一步在执行层使用字典压缩后的值加速查询计算,在低基数字符串过滤、聚合、排序等场景中获得执行加速性能提升,根据SQL模式的不同,可以在执行中获得10%至200%的性能提升。

字典编码执行加速功能由参数adbpg_enable_encode_optimize控制,默认配置为OFF(关闭)。对于适合使用字典编码执行加速的SQL,优化器会自动生成对应的优化计划。例如,对于如下SQL:

SELECT
	n_name,
	max(n_regionkey)
FROM
	nation
WHERE
	n_name > 'ALGERIA'
	AND n_name < 'FRANCE'
GROUP BY
	n_name;

使用EXPLAIN查看执行计划,可以看到计划中使用的编码方法,并引入了Decode算子进行计算过程中的解码。

 QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Gather Motion 3:1  (slice1; segments: 3)  (cost=76.72..78.27 rows=81 width=36)
   ->  GroupAggregate  (cost=76.72..77.19 rows=27 width=36)
         Group Key: n_name
         ->  Sort  (cost=76.72..76.79 rows=28 width=36)
               Sort Key: n_name
               ->  Redistribute Motion 3:3  (slice2; segments: 3)  (cost=0.00..76.05 rows=28 width=36)
                     Hash Key: n_name
                     ->  Decode  (cost=0.00..75.50 rows=28 width=36)
                           Params: dictKeyIdx[1], dictID[0]
                           ->  Seq Scan on nation  (cost=0.00..75.50 rows=28 width=36)
                                 Filter: ((ordered_encode(n_name) > ('ALGERIA'::text)::integer with dict ID 0) AND (ordered_encode(n_name) < ('FRANCE'::text)::integer with dict ID 0))
 Optimizer: Postgres query optimizer
(12 rows)

相比较字符串类型的数据运算,字典编码后的整型数据运算可以有效地提升查询性能;同时优化器会在执行计划中合适的位置插入Decode算子,对存储层读取到的字典编码数据进行解码,保证执行的准确性。

使用自动压缩编码优化

Beam存储引擎支持自适应的压缩算法。如果您不确定是否需要使用字典编码压缩,可以在表级别指定compresstype = 'auto',存储引擎会自动选择合适的编码压缩算法。

CREATE TABLE NATION (
    n_nationkey integer NOT NULL,
    n_name character(25) NOT NULL,
    n_regionkey integer NOT NULL,
    n_comment character varying(152)
)
USING beam WITH (compresstype = 'auto') 
DISTRIBUTE by (n_nationkey);

nation表使用自动压缩编码后,各列的编码如下所示:

数据类型

编码方法

说明

n_nationkey

integer

LZ4编码或int编码

整型数据,默认使用LZ4编码或int编码。

n_name

character(25)

字典编码

n_name列是一系列国家的名字,是固定范围的字符串集合,适合使用字典编码。

n_regionkey

integer

LZ4编码或int编码

整型数据,默认使用LZ4编码或int编码。

n_comment

character varying(152)

字典编码

n_comment列包含不重复的字符串数据,当数据行数超过编码阈值(255)后,n_comment列将不会使用字典编码,而是尝试使用LZ4编码。

字典编码支持Delta

Beam存储引擎包含两部分:

  • 用以应对实时写入的行存格式的Delta。

  • 用以应对批量写入和大量扫描场景的基于PAX结构的列存格式的Base。

重要

v7.0.2.3及以上版本的实例支持行列混存Base。

v7.0.4.0及以上版本的实例字典编码适配行存格式Delta,提高了字典编码的易用性。

例如,执行如下SQL,使用EXPLAIN查看执行计划,可以看到计划中使用Decode算子进行计算过程中的解码,可以提高数据处理效率及数据安全性,进而提高字典编码的易用性。

 CREATE TABLE test_auto(a int, b text) 
 using beam with(compresstype=auto,compresslevel=5);

 INSERT INTO test_auto values (1,'adbpg1'),(2,'adbpg2'), (3, 'adbpg3'),(10,'adbpg10');

SET adbpg_enable_encode_optimize to ON;

 explain SELECT * FROM test_auto WHERE b='adbpg1';
 -----------------------------------------------------------------------------
 Gather Motion 3:1  (slice1; segments: 3)  (cost=0.00..2.04 rows=1 width=36)
   ->  Decode  (cost=0.00..2.02 rows=1 width=36)
         Params: dictKeyIdx[2], dictID[0]
         ->  Seq Scan on test_auto  (cost=0.00..2.02 rows=1 width=36)
               Filter: (encode(b) = ('adbpg1'::text)::integer with dict ID 0)
 Optimizer: Postgres-based planner