数据库的列值压缩与编码优化技术
字数 3359 2025-12-13 00:49:54
好的,我们来看一个新的题目。今天要讲解的题目是:
数据库的列值压缩与编码优化技术
1. 题目/知识点描述
在数据库的列式存储或行列混合存储架构中,除了选择合适的数据布局,对存储在磁盘或内存中的数据进行高效的压缩与编码是提升性能、降低存储成本的核心技术之一。与传统的通用压缩算法(如Gzip、LZ4)不同,列值压缩与编码技术深度结合数据本身的特征(如数据类型、取值范围、分布规律、基数等),设计精巧的压缩算法,旨在实现更高的压缩比、更快的压缩/解压速度、以及直接在压缩数据上进行查询操作(零拷贝或延迟物化)。
本知识点将深入探讨几种主流的列值压缩与编码技术,解释它们的原理、适用场景、以及如何在查询执行中被优化利用。
2. 循序渐进讲解
步骤一:为什么要对列值进行专门压缩?
- 背景:传统的行式存储,一条记录的所有字段紧挨着存放。压缩时,通常以数据页(Page)或块(Block)为单位使用通用压缩算法。由于相邻字段数据类型差异大,数据的局部相似性低,压缩效果有限。
- 列式存储的优势:在列式存储中,同一列的所有值被连续存储在一起。这些值通常具有相同的数据类型、相似的数据范围,甚至存在大量重复值或有序序列。这种高度的数据局部性和同质性为设计高效的专用压缩算法提供了绝佳的条件。
- 核心目标:
- 减少I/O:压缩后,从磁盘读取相同行数所需的数据量大大减少,直接提升扫描性能。
- 增加内存缓存效率:更多数据可以驻留在内存缓存(如Buffer Pool)中,提高缓存命中率。
- 降低存储成本:用更少的空间存储更多数据。
- 加速计算:某些编码方式允许查询引擎直接在压缩数据上进行操作(如比较、聚合),无需完全解压,即“延迟物化”或“计算下推至压缩层”。
步骤二:关键评估维度
在选择或理解一种压缩编码技术时,我们需要从以下几个维度考量:
- 压缩比:压缩后大小 / 压缩前大小。
- 压缩/解压速度:尤其是解压速度,对查询延迟至关重要。
- 是否支持直接计算:能否在不完全解压为原始值的情况下,进行谓词过滤、聚合等操作。
- 适用场景:对数据特征有何要求(如有序、基数低等)。
步骤三:主流列值压缩与编码技术详解
技术一:字典编码
- 过程描述:
- 扫描与分析:扫描整列数据,收集所有不重复的值,构成一个“字典”。
- 构建映射:为字典中的每个唯一值分配一个紧凑的整数ID(通常是按频率排序后,高频值用短ID)。
- 转换存储:将原始列中的每一个值,替换为其对应的整数ID,形成一个
ID序列。原始字典单独存储。
- 示例:
- 原始字符串列:
[“男”, “女”, “男”, “男”, “女”] - 字典:
{0: “男”, 1: “女”} - 编码后ID序列:
[0, 1, 0, 0, 1](可以用更小的数据类型如tinyint存储)
- 原始字符串列:
- 特点与优化:
- 适用场景:列基数低(唯一值少),如状态、性别、省份等枚举型字段。压缩比极高。
- 支持直接计算:对于等值过滤 (
WHERE gender = ‘男’),查询引擎只需将过滤条件’男’转换为字典ID0,然后在压缩的ID序列上直接比较ID == 0即可,无需解压字符串。对于GROUP BY,直接在ID上分组,最后再通过字典“翻译”回原始值输出。 - 扩展:可以结合行程编码进一步压缩。如果ID序列是
[0,0,0,1,1,1],可以编码为(0, 3), (1, 3),对有序或重复序列多的列效果极佳。
技术二:行程编码
- 过程描述:将连续重复出现的相同值,压缩为一个
(值, 连续出现次数)的元组。 - 示例:
- 原始列:
[100, 100, 100, 200, 200, 150] - RLE编码后:
[(100,3), (200,2), (150,1)]
- 原始列:
- 特点与优化:
- 适用场景:数据高度有序或包含大量连续重复值,例如按时间排序的传感器状态列、排序列。
- 支持直接计算:对于范围查询 (
WHERE value > 120),引擎可以扫描元组,直接比较值,并结合次数快速跳过大量不满足条件的行,实现了数据跳跃。聚合函数如SUM,可以计算为100*3 + 200*2 + 150*1,效率很高。 - 常作为二级编码:常与字典编码、位图编码等结合使用,压缩它们产生的整数序列。
技术三:位图编码
- 过程描述:
- 为列中的每个可能的值(或字典编码后的每个ID)创建一个独立的位图(Bit Map)。
- 位图的长度等于总行数。如果第i行的值等于该值,则对应位图中第i位为1,否则为0。
- 示例(接字典编码后的ID序列
[0,1,0,0,1]):- 值“男”(ID=0)的位图:
1 0 1 1 0 - 值“女”(ID=1)的位图:
0 1 0 0 1
- 值“男”(ID=0)的位图:
- 特点与优化:
- 适用场景:基数极低的列(通常<100)。是大数据分析中
WHERE ... IN (...)或多个等值条件AND/OR组合过滤的利器。 - 支持直接计算:过滤操作转化为高效的位图运算。
WHERE gender = ‘男’ AND city = ‘北京’:只需对“男”和“北京”两个位图做按位与(AND) 操作,结果为1的行就是满足条件的行。速度极快。COUNT(DISTINCT gender):只需统计有多少个位图不全为0。
- 存储优化:位图本身可以使用位图压缩算法(如WAH, EWAH, Roaring Bitmap)进一步压缩,使其在高基数下也具有一定实用性。
- 适用场景:基数极低的列(通常<100)。是大数据分析中
技术四:增量编码与帧间压缩
- 过程描述:适用于数值列,尤其是按某列(如时间)排序后。不存储原始值,而是存储当前值与上一个值(或某个基准值)的差值。
- 示例(按时间有序):
- 原始时间戳列:
[1000, 1005, 1010, 1020, 1021] - 增量编码后:
[1000, 5, 5, 10, 1](第一个存基准值,后面存差值)
- 原始时间戳列:
- 特点与优化:
- 适用场景:有序的、值变化平缓的数值列,如时序数据、自增主键。
- 压缩效果:差值通常比原始值小得多,可以用更少的数据位(如1字节而非8字节)来存储,压缩比高。
- 支持直接计算:对于范围查询,需要在差值序列上累积计算还原部分值,但结合索引可以高效定位。更重要的应用是加速扫描和聚合,因为CPU处理更小的整数更快。
技术五:轻量级通用压缩作为最后一步
- 过程描述:在经过上述语义感知的编码之后,得到的整数序列(如字典ID、RLE元组、增量值)可能仍然存在一定的冗余。此时,可以对其应用一个速度极快的轻量级通用压缩算法,如LZ4、Snappy或Zstd。
- 作用:这属于“锦上添花”。这些算法速度极快,可以消除编码后数据中剩余的微小冗余,进一步减少I/O,而其解压开销与从编码数据直接计算带来的收益相比,通常是可以接受的。
3. 总结与在查询执行中的优化
现代分析型数据库(如Apache Parquet、ORC文件格式,以及ClickHouse、Vertica等数据库)会智能地结合多种编码技术:
- 按列自动选择:优化器会根据收集的列统计信息(基数、是否排序、数据分布),为每一列选择最合适的一种或多种编码组合。例如,一个低基数字符串列可能采用“字典编码 -> RLE -> 位图压缩”。
- 查询执行下推:这是最大的性能收益点。查询引擎的运算符(如扫描、过滤、聚合)被设计为能够理解这些编码格式。它们尽可能地在压缩或编码后的数据上直接操作,避免过早、全量的解压。例如,一个在字典编码列上的
GROUP BY操作,其哈希表可以直接在整数ID上构建,大幅减少内存占用和计算量。 - 向量化执行结合:编码后的数据(尤其是整数序列)非常适合于向量化处理(SIMD指令)。CPU可以一次性加载和处理一批编码值,实现极高的吞吐量。
综上所述,列值压缩与编码优化技术,是列式存储发挥性能优势的基石。它不仅仅是“压缩”,更是一种数据重组织,使得数据存储格式更加贴合后续的分析型查询模式,从而在I/O、内存、CPU计算等多个层面实现数量级的性能提升。