Redshift 数据仓库:列式存储与分布键
Redshift 数据仓库核心概念:列式存储与分布键
Amazon Redshift 是一款完全托管的 PB 级云数据仓库,专为大规模并行处理(MPP)而设计。理解其底层两大基石——列式存储与分布键——是高效建模和查询优化的关键。本教程将深入剖析这两个概念,并提供最佳实践指南,帮助你构建高性能的数据仓库。
一、为什么选择 Redshift?
在深入技术细节之前,我们先明确 Redshift 适合解决什么问题。当你的数据量达到 TB 甚至 PB 级别,传统关系型数据库(如 MySQL、PostgreSQL)的查询性能会急剧下降。Redshift 通过以下方式实现极速分析:
- 列式存储:针对聚合查询大幅降低 I/O。
- 并行处理:将查询任务分发到多个计算节点。
- 智能压缩:按列特性选择最高效的压缩算法。
- 结果缓存:对重复查询自动返回缓存结果。
在本教程中,我们聚焦于列式存储如何减少扫描数据量,以及分布键如何最小化节点间的网络传输。
二、深入列式存储
什么是列式存储?
传统数据库(如 PostgreSQL)以行式组织数据,将一行中所有字段顺序存放在一起。Redshift 则采用列式组织,将表中每一列的数据独立存储。
假设我们有一张用户行为表,包含三列:user_id、event_date、page_views。
- 行式存储磁盘布局:
[1001, 2024-01-01, 5], [1002, 2024-01-02, 8], [1003, 2024-01-02, 12], ...
- 列式存储磁盘布局:
user_id 数据块: [1001, 1002, 1003, ...]
event_date 数据块: [2024-01-01, 2024-01-02, 2024-01-02, ...]
page_views 数据块: [5, 8, 12, ...]
列式存储的核心优势
- 减少 I/O:聚合查询往往只需访问少数几列。例如,计算总浏览量
SELECT SUM(page_views) FROM events,只需扫描page_views列所在的数据块,完全忽略其他列。相比行式存储必须读取全部字段,I/O 量可减少 90% 以上。 - 高效压缩:同一列的数据类型和取值范围高度一致,压缩算法效果极佳。Redshift 会自动为每一列选择最佳压缩编码(如 Runlength、Delta、Zstandard)。高压缩率不仅节省存储成本,还因为读取更少的数据块而提升查询速度。
- 向量化处理:CPU 可一次性对同一列的一批数据执行相同操作,充分利用现代 CPU 的 SIMD 指令集,加速过滤、运算和聚合。
Redshift 数据块与区域映射
Redshift 将列数据划分为 1 MB 的不可变块,并保存在区域映射中:记录每个块的最小值、最大值等元数据。当查询包含 WHERE event_date >= '2024-01-15' 时,优化器会先检查区域映射,直接跳过那些最大值仍小于 2024-01-15 的块,实现精细化的裁剪查询,这是列式存储高效的另一重保障。
三、理解 Redshift 的分布键
Redshift 由领导节点和多个计算节点组成。计算节点又划分为若干切片(Slice),每个切片独立处理部分数据。当数据被加载到表中时,需要决定如何在切片之间分布行,这就引出了分布键的概念。
三种分布方式
创建表时,DISTSTYLE 子句定义了分布策略:
DISTSTYLE KEY (列):指定某一列作为分布键,其哈希值决定该行所属的切片。相同分布键值的记录必然落在同一节点。DISTSTYLE EVEN:默认方式,行以轮询方式均匀分布到所有切片。没有明确的分布键。DISTSTYLE ALL:将整个表完整复制到所有计算节点。通常用于极小表(维度表),避免广播开销。DISTSTYLE AUTO:让 Redshift 自动选择。通常不建议依赖,理解原理后手动指定更可靠。
分布键对连接(JOIN)性能的决定性作用
MPP 架构下,连接操作会引发数据在节点间移动,这是最耗资源的操作之一。数据移动有两种类型:
- 广播(Broadcast):将其中一张表的全部副本发送到每个节点。适用于一张表很小的情况。
- 重组分布(Redistribution):两张表都根据连接键重新分布。如果无表采用连接键作为分布键,则两张表的数据都需要跨网络传输。
最佳场景:当连接键同时也是两张表的分布键时,由于相同键值的行已在同一节点,连接可以完全本地执行,无需网络传输,性能最高。
选择分布键的黄金法则
- 以连接键为首选:选择经常在大表连接中使用的列。例如,数据仓库最常见的是事实表与维度表连接,且事实表通常很大,因此应选择事实表中连接维度表的键作为分布键(如
customer_id、product_id)。 - 避免数据倾斜:如果某个键值的行数占比过高(例如
user_id中的NULL或超级用户),会导致某些切片数据量远超其他切片,产生“热切片”,降低并行效率。应确保分布键的基数足够高,且值分布均匀。 - 兼顾分组与聚合:查询中频繁出现
GROUP BY的列,若与分布键相同,聚合可在本地执行,避免再次重分布(Redshift 会在GROUP BY键与分布键一致时启用流聚合)。 - 不要使用日期或时间戳列:这类列通常用作过滤条件,而不是连接键;分布到该列会导致数据按时间顺序写入,易引起热点;更重要的是,典型查询需要扫描时间范围,数据如果按时间分布,每个切片都能参与并行扫描,反而是优势。因此,时间列适合作为排序键,不适合作为分布键。
实战示例
假设你的电商数据模型包含:
- 订单事实表
orders(数十亿行),列:order_id,customer_id,order_date,amount - 客户维度表
customers(百万行),列:customer_id,name,segment
最优设计:
CREATE TABLE customers (
customer_id BIGINT NOT NULL,
name VARCHAR(100),
segment VARCHAR(50)
)
DISTSTYLE ALL; -- 维度表全复制,避免80%的连接传输
CREATE TABLE orders (
order_id BIGINT NOT NULL,
customer_id BIGINT NOT NULL,
order_date DATE,
amount DECIMAL(18,2)
)
DISTSTYLE KEY
DISTKEY(customer_id) -- 事实表按 customer_id 分布
COMPOUND SORTKEY(order_date, customer_id); -- 排序键优化时间范围查询
这样,当执行 SELECT c.segment, SUM(o.amount) FROM orders o JOIN customers c ON o.customer_id = c.customer_id WHERE o.order_date BETWEEN '2024-01-01' AND '2024-01-31' GROUP BY c.segment 时:
customers存在于每个节点,无需传输。orders已按customer_id分布,与customers的连接在本地完成。group by c.segment需要轻度聚合,Redshift 会对本地数据先做预聚合再汇总,大幅减少数据移动。
四、列式存储与分布键的协同效应
优秀的设计让两者优势叠加:
- 列式存储让只读取
amount和order_date列的扫描飞快。 - 分布键使连接无数据移动,避免网络瓶颈。
- 排序键(Sort Key)允许对
order_date进行范围裁剪。 每一层优化都在减少不必要的 IO 和网络消耗,最终实现亚秒级查询响应,即使在数百 TB 数据规模下。
五、常见误区和监控
- 误区1:分布键应为主键。主键通常为唯一标识,但若主键不是连接键,用它分布将导致几乎所有 JOIN 都涉及重分布。选择连接键,而不是 ID。
- 误区2:EVEN 分布最安全。对于大表,EVEN 分布虽然消除了倾斜风险,但也让所有大表连接都必须跨节点传输,性能最差。只在确实找不到合适分布键的小表上使用。
- 误区3:忽略排序键。分布键解决“行在哪个节点”,排序键解决“行在节点内怎么排序”。两者独立,需分别优化。
监控查询:审核查询计划中的 DS_DIST_ALL_NONE(无传输)和 DS_DIST_BOTH(两表都重分布),倾向于前者。使用 STL_ALERT_EVENT_LOG 检查数据倾斜。
六、动手练习:验证你的选择
- 在 Redshift 中创建三张测试表:一张按连接键分布,一张按 EVEN 分布,一张错误分布。
- 加载具有倾斜特征的模拟数据。
- 使用
EXPLAIN分析同一个 JOIN 查询的计划,观察XN Hashed Join与XN Hash Join的区别及数据移动操作。 - 使用
SVV_TABLE_INFO查看每个表的倾斜情况(skew_rows列)。
通过实际对比,你会更加深刻地理解分布键的威力。
七、总结
- 列式存储是 Redshift 高性能的基础,通过只读相关列、高压缩率和元数据裁剪来最小化磁盘 I/O。
- 分布键决定了数据在集群切片间的放置方式,正确选择可以消除连接时的网络 I/O,实现本地计算。
- 设计时始终从事实表与维度表的连接键出发,选择基数高、分布均匀且频繁出现在 JOIN 和 GROUP BY 中的列作为分布键。
- 将小维度表设为
DISTSTYLE ALL,大事实表使用KEY分布,并配合适当的排序键,搭建稳健的数据仓库模型。
现在,你可以打开 Redshift 查询编辑器,开始用这些原则优化自己的表结构了。记住:没有一劳永逸的分布键,持续监控查询性能并调整才是正道。