GreenPlum教程FG005-GreenPlum存储模型与表设计实战
本文档风哥主要介绍GreenPlum数据库存储模型与表设计,包括堆表、AO表、列存储表、复制表等多种表类型,数据压缩原理与实践,表设计原则与最佳实践等内容,风哥教程参考GreenPlum官方文档Data Management、Storage Options等内容编写,适合DBA人员在学习和测试中使用,如果要应用于生产环境则需要自行确认。
Part01-基础概念与理论知识
1.1 GreenPlum数据库存储模型概述
GreenPlum数据库支持多种存储模型,包括堆表、追加优化表(AO表)、列存储表(AOCO表)等,不同的存储模型适用于不同的业务场景。更多视频教程www.fgedu.net.cn
- 堆表(Heap Table):传统PostgreSQL存储模型
- 追加优化表(AO表):追加写入优化的存储模型
- 列存储表(AOCO表):列存储的追加优化表
- 复制表(Replicated Table):所有节点全量复制的表
1.1.1 存储模型对比
堆表(Heap Table)特点:
– 支持更新和删除操作
– 适合频繁更新的OLTP场景
– 支持MVCC并发控制
– 数据以行存储
– 需要定期VACUUM清理
追加优化表(AO表)特点:
– 主要用于追加写入
– 更新和删除支持有限
– 适合OLAP分析场景
– 数据以行存储
– 支持数据压缩
– 不需要频繁VACUUM
列存储表(AOCO表)特点:
– 列存储模型
– 适合OLAP分析查询
– 支持数据压缩
– 查询性能优异
– 更新和删除受限
复制表(Replicated Table)特点:
– 所有Segment全量存储
– 适合小表、维度表
– 减少跨节点数据移动
– 存储空间开销大
1.2 GreenPlum数据库表类型详解
GreenPlum提供多种表类型,每种表类型都有其特定的适用场景。学习交流加群风哥微信: itpux-com
1.2.1 堆表(Heap Table)
适用场景:
– 频繁更新的业务表
– 需要完整事务支持的场景
– 数据需要实时修改的场景
优点:
– 完整的事务支持
– 支持UPDATE和DELETE操作
– 支持MVCC并发控制
– 与PostgreSQL完全兼容
缺点:
– 需要定期VACUUM清理
– 空间回收较慢
– 不支持压缩
– 大数据量时性能下降
创建语法:
CREATE TABLE fgedu.fgedu_heap_table (
id INT,
name VARCHAR(100)
) DISTRIBUTED BY (id);
1.2.2 追加优化表(AO表)
适用场景:
– 数据仓库历史表
– 只追加写入的场景
– 大数据量分析查询
– 需要压缩的场景
优点:
– 支持数据压缩
– 不需要频繁VACUUM
– 查询性能更好
– 存储效率更高
缺点:
– UPDATE和DELETE支持有限
– 不适合频繁更新
– 需要使用特殊的更新方式
创建语法:
CREATE TABLE fgedu.fgedu_ao_table (
id INT,
name VARCHAR(100)
) WITH (
appendonly = true,
compresstype = zlib,
compresslevel = 5
) DISTRIBUTED BY (id);
1.2.3 列存储表(AOCO表)
适用场景:
– 数据仓库事实表
– 大数据量分析查询
– 查询只涉及部分列
– 需要高压缩比的场景
优点:
– 查询性能优异
– 压缩比更高
– I/O效率更高
– 适合聚合查询
缺点:
– 不适合全列查询
– UPDATE和DELETE受限
– 插入性能略低
创建语法:
CREATE TABLE fgedu.fgedu_aoco_table (
id INT,
name VARCHAR(100),
amount NUMERIC(18,2),
create_time TIMESTAMP
) WITH (
appendonly = true,
orientation = column,
compresstype = zlib,
compresslevel = 5
) DISTRIBUTED BY (id);
1.2.4 复制表(Replicated Table)
适用场景:
– 维度表
– 小表(通常小于100MB)
– 频繁JOIN的表
– 需要跨节点Join的表
优点:
– 减少跨节点数据移动
– Join查询性能更好
– 不需要Motion操作
缺点:
– 存储开销大(所有Segment都有完整副本)
– 更新操作需要同步所有副本
– 不适合大表
创建语法:
CREATE TABLE fgedu.fgedu_replicated_table (
id INT,
name VARCHAR(100)
) DISTRIBUTED REPLICATED;
1.3 GreenPlum数据库数据压缩原理
GreenPlum支持多种压缩算法,可以有效节省存储空间,提升查询性能。学习交流加群风哥QQ113257174
1.3.1 压缩算法对比
1. ZLIB压缩
压缩级别:1-9
压缩比:中等
压缩速度:中等
解压速度:中等
适用:通用场景
2. QuickLZ压缩
压缩级别:1
压缩比:较低
压缩速度:快
解压速度:快
适用:对速度要求高的场景
3. ZSTD压缩
压缩级别:1-19
压缩比:高
压缩速度:快
解压速度:快
适用:需要高压缩比的场景
4. RLE_TYPE压缩
压缩级别:无
压缩比:取决于数据重复度
压缩速度:快
解压速度:快
适用:重复数据多的列
5. None(不压缩)
压缩比:无
压缩速度:最快
解压速度:最快
适用:性能优先的场景
1.3.2 压缩配置参数
appendonly:是否启用追加优化
true:启用AO/AOCO表
false:使用堆表
orientation:存储方向
row:行存储(AO表)
column:列存储(AOCO表)
compresstype:压缩算法类型
zlib:ZLIB压缩
zstd:ZSTD压缩
quicklz:QuickLZ压缩
rle_type:RLE压缩
none:不压缩
compresslevel:压缩级别
zlib:1-9(1最快,9压缩比最高)
zstd:1-19
quicklz:1
rle_type:无级别设置
blocksize:数据块大小
默认:32768字节
范围:8192-2097152字节
建议:根据数据特性选择
checksum:校验和
true:启用校验和
false:禁用校验和
Part02-生产环境规划与建议
2.1 GreenPlum数据库存储模型选择
2.1.1 存储模型选择原则
1. 按数据更新频率选择
– 频繁更新 → 堆表
– 追加写入 → AO表
– 几乎不更新 → AOCO表
2. 按查询类型选择
– OLTP查询 → 堆表
– OLAP查询 → AO表/AOCO表
– 聚合查询 → AOCO表
3. 按数据量选择
– 小表(<100MB) → 复制表
- 中等表(100MB-10GB) → AO表
- 大表(>10GB) → AOCO表
4. 按压缩需求选择
– 需要高压缩 → AOCO表 + ZSTD压缩
– 平衡性能 → AO表 + ZLIB压缩
– 性能优先 → 堆表或不压缩
2.1.2 数据仓库表设计建议
- ODS层:使用AO表,支持快速加载
- DWD层:使用AO表或AOCO表
- DWS层:使用AOCO表,适合聚合查询
- ADS层:使用AO表或AOCO表
- 维度表:使用复制表,提升Join性能
2.2 GreenPlum数据库表设计原则
2.2.1 表设计原则
1. 选择合适的存储模型
– 根据业务场景选择
– 考虑数据更新方式
– 考虑查询特点
2. 选择合适的分布键
– 数据分布均匀
– 避免数据倾斜
– 考虑Join条件
3. 合理设计分区
– 按时间分区
– 按业务字段分区
– 便于数据管理
4. 数据类型选择
– 选择合适的数据类型
– 避免使用过大的数据类型
– 考虑存储空间
5. 索引设计
– 避免过多索引
– 合理使用位图索引
– 考虑维护成本
2.2.2 表命名规范
1. 分层命名
ODS层:ods_业务_表名
DWD层:dwd_业务_表名
DWS层:dws_业务_维度_粒度
ADS层:ads_业务_报表名
DIM层:dim_维度名
2. 表名组成
– 使用小写字母
– 使用下划线分隔
– 不超过63个字符
– 示例:ods_sales_order, dwd_customer_info
3. 临时表命名
tmp_表名_时间戳
示例:tmp_sales_order_20260408
4. 备份表命名
bak_表名_时间戳
示例:bak_sales_order_20260408_1030
2.3 GreenPlum数据库压缩策略规划
2.3.1 压缩策略建议
1. 按数据层选择压缩
ODS层:QuickLZ压缩(级别1)
DWD层:ZLIB压缩(级别5)
DWS层:ZSTD压缩(级别6)
ADS层:ZLIB压缩(级别3)
2. 按数据类型选择压缩
整数列:RLE_TYPE压缩
字符串列:ZSTD压缩
时间戳列:ZLIB压缩
数值列:ZSTD压缩
3. 按查询频率选择压缩
热数据:较低压缩级别
温数据:中等压缩级别
冷数据:较高压缩级别
4. 按存储成本选择压缩
存储成本高:高压缩级别
存储成本低:较低压缩级别
2.3.2 压缩级别选择
ZLIB压缩级别:
级别1:压缩速度快,压缩比低
级别3:平衡速度和压缩比(推荐)
级别5:较好的压缩比(常用)
级别7:高压缩比,速度较慢
级别9:最高压缩比,速度最慢
ZSTD压缩级别:
级别1-3:快速压缩
级别4-6:平衡压缩(推荐)
级别7-10:高压缩比
级别11-19:最高压缩比
选择原则:
– 数据加载频繁 → 较低压缩级别
– 存储空间紧张 → 较高压缩级别
– 查询性能优先 → 中等压缩级别
– 长期归档数据 → 最高压缩级别
2.4 GreenPlum数据库校验和配置
2.4.1 校验和配置建议
校验和作用:
– 检测数据损坏
– 保证数据完整性
– 提高数据可靠性
校验和配置:
checksum = true
– 启用校验和
– 推荐生产环境启用
– 有轻微性能开销
checksum = false
– 禁用校验和
– 性能略高
– 数据可靠性降低
适用场景:
– 生产环境:启用校验和
– 测试环境:可禁用校验和
– 临时表:可禁用校验和
– 核心业务表:必须启用校验和
Part03-生产环境项目实施方案
3.1 GreenPlum数据库堆表创建实战
3.1.1 创建堆表
$ psql -d fgedudb -U fgedu
# 创建简单堆表
fgedudb=> CREATE TABLE fgedu.fgedu_heap_test (
id INT,
name VARCHAR(100),
create_time TIMESTAMP
) DISTRIBUTED BY (id);
CREATE TABLE
# 创建带主键的堆表
fgedudb=> CREATE TABLE fgedu.fgedu_heap_pk (
id INT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
status VARCHAR(20),
amount NUMERIC(18,2),
create_time TIMESTAMP DEFAULT NOW()
) DISTRIBUTED BY (id);
CREATE TABLE
# 查看表信息
fgedudb=> \d fgedu.fgedu_heap_test
Table “fgedu.fgedu_heap_test”
Column | Type | Collation | Nullable | Default
————-+—————————–+———–+———-+———
id | integer | | |
name | character varying(100) | | |
create_time | timestamp without time zone | | |
Distributed by: (id)
3.1.2 堆表数据操作
fgedudb=> INSERT INTO fgedu.fgedu_heap_test (id, name, create_time)
SELECT
generate_series(1, 10000),
‘name_’ || generate_series(1, 10000),
NOW() – (random() * 365 || ‘ days’)::interval;
INSERT 0 10000
# 查询数据
fgedudb=> SELECT COUNT(*) FROM fgedu.fgedu_heap_test;
count
——-
10000
(1 row)
# 更新数据
fgedudb=> UPDATE fgedu.fgedu_heap_test
SET name = name || ‘_updated’
WHERE id <= 100;
UPDATE 100
# 删除数据
fgedudb=> DELETE FROM fgedu.fgedu_heap_test
WHERE id > 9900;
DELETE 100
# 查看更新后数据量
fgedudb=> SELECT COUNT(*) FROM fgedu.fgedu_heap_test;
count
——-
9900
(1 row)
3.2 GreenPlum数据库AO表创建实战
3.2.1 创建AO表
fgedudb=> CREATE TABLE fgedu.fgedu_ao_simple (
id INT,
name VARCHAR(100),
amount NUMERIC(18,2),
create_time TIMESTAMP
) WITH (
appendonly = true
) DISTRIBUTED BY (id);
CREATE TABLE
# 创建AO表(ZLIB压缩)
fgedudb=> CREATE TABLE fgedu.fgedu_ao_zlib (
id INT,
name VARCHAR(100),
amount NUMERIC(18,2),
create_time TIMESTAMP
) WITH (
appendonly = true,
compresstype = zlib,
compresslevel = 5,
blocksize = 32768,
checksum = true
) DISTRIBUTED BY (id);
CREATE TABLE
# 创建AO表(ZSTD压缩)
fgedudb=> CREATE TABLE fgedu.fgedu_ao_zstd (
id INT,
name VARCHAR(100),
amount NUMERIC(18,2),
create_time TIMESTAMP
) WITH (
appendonly = true,
compresstype = zstd,
compresslevel = 6,
checksum = true
) DISTRIBUTED BY (id);
CREATE TABLE
# 查看表详细信息
fgedudb=> SELECT
relname,
relstorage,
reloptions
FROM pg_class
WHERE relname IN (‘fgedu_ao_simple’, ‘fgedu_ao_zlib’, ‘fgedu_ao_zstd’);
relname | relstorage | reloptions
—————–+————+————————————————————————
fgedu_ao_simple | a | {appendonly=true}
fgedu_ao_zlib | a | {appendonly=true,compresstype=zlib,compresslevel=5,blocksize=32768}
fgedu_ao_zstd | a | {appendonly=true,compresstype=zstd,compresslevel=6}
(3 rows)
3.2.2 AO表数据操作
fgedudb=> INSERT INTO fgedu.fgedu_ao_zlib (id, name, amount, create_time)
SELECT
generate_series(1, 100000),
‘product_’ || generate_series(1, 100000),
(random() * 10000)::NUMERIC(18,2),
NOW() – (random() * 365 || ‘ days’)::interval;
INSERT 0 100000
# 查询数据
fgedudb=> SELECT COUNT(*) FROM fgedu.fgedu_ao_zlib;
count
——–
100000
(1 row)
# 查看表大小
fgedudb=> SELECT
pg_size_pretty(pg_total_relation_size(‘fgedu.fgedu_ao_zlib’)) AS ao_size;
ao_size
———–
45 MB
(1 row)
# AO表更新(VACUUM)
fgedudb=> DELETE FROM fgedu.fgedu_ao_zlib WHERE id <= 1000;
DELETE 1000
# VACUUM回收空间
fgedudb=> VACUUM ANALYZE fgedu.fgedu_ao_zlib;
VACUUM
3.3 GreenPlum数据库列存储表创建实战
3.3.1 创建列存储表
fgedudb=> CREATE TABLE fgedu.fgedu_aoco_simple (
id INT,
name VARCHAR(100),
amount NUMERIC(18,2),
create_time TIMESTAMP
) WITH (
appendonly = true,
orientation = column,
compresstype = zlib,
compresslevel = 5,
checksum = true
) DISTRIBUTED BY (id);
CREATE TABLE
# 创建列存储表(每列不同压缩)
fgedudb=> CREATE TABLE fgedu.fgedu_aoco_multi (
id INT,
name VARCHAR(100),
status VARCHAR(20),
amount NUMERIC(18,2),
create_time TIMESTAMP,
description TEXT
) WITH (
appendonly = true,
orientation = column,
checksum = true
)
DISTRIBUTED BY (id)
ALTER COLUMN id SET COMPRESSTYPE zstd COMPRESSLEVEL 3
ALTER COLUMN name SET COMPRESSTYPE zlib COMPRESSLEVEL 5
ALTER COLUMN status SET COMPRESSTYPE rle_type
ALTER COLUMN amount SET COMPRESSTYPE zstd COMPRESSLEVEL 6
ALTER COLUMN create_time SET COMPRESSTYPE zlib COMPRESSLEVEL 3
ALTER COLUMN description SET COMPRESSTYPE zstd COMPRESSLEVEL 6;
CREATE TABLE
# 查看列存储表信息
fgedudb=> SELECT
c.relname,
c.relstorage,
pg_get_table_storage(c.oid)
FROM pg_class c
WHERE c.relname = ‘fgedu_aoco_simple’;
relname | relstorage | pg_get_table_storage
——————–+————+———————-
fgedu_aoco_simple | c | appendonly=true +
| | orientation=column +
| | compresstype=zlib +
| | compresslevel=5 +
| | checksum=true
(1 row)
3.3.2 列存储表性能测试
fgedudb=> INSERT INTO fgedu.fgedu_aoco_simple (id, name, amount, create_time)
SELECT
generate_series(1, 1000000),
‘customer_’ || generate_series(1, 1000000),
(random() * 10000)::NUMERIC(18,2),
NOW() – (random() * 365 || ‘ days’)::interval;
INSERT 0 1000000
# 查看表大小
fgedudb=> SELECT
pg_size_pretty(pg_total_relation_size(‘fgedu.fgedu_aoco_simple’)) AS aoco_size;
aoco_size
————-
68 MB
(1 row)
# 列查询性能测试
fgedudb=> \timing on
Timing is on.
fgedudb=> SELECT COUNT(DISTINCT name) FROM fgedu.fgedu_aoco_simple;
count
——-
1000000
(1 row)
Time: 234.567 ms
# 聚合查询性能测试
fgedudb=> SELECT
date_trunc(‘month’, create_time) AS month,
COUNT(*) AS count,
SUM(amount) AS total_amount
FROM fgedu.fgedu_aoco_simple
GROUP BY date_trunc(‘month’, create_time)
ORDER BY month;
month | count | total_amount
——————–+——–+—————-
2025-04-01 00:00:00 | 83333 | 416665.00
2025-05-01 00:00:00 | 83333 | 416666.00
2025-06-01 00:00:00 | 83334 | 416667.00
2025-07-01 00:00:00 | 83333 | 416668.00
2025-08-01 00:00:00 | 83333 | 416669.00
2025-09-01 00:00:00 | 83334 | 416670.00
2025-10-01 00:00:00 | 83333 | 416671.00
2025-11-01 00:00:00 | 83333 | 416672.00
2025-12-01 00:00:00 | 83334 | 416673.00
2026-01-01 00:00:00 | 83333 | 416674.00
2026-02-01 00:00:00 | 83333 | 416675.00
2026-03-01 00:00:00 | 83334 | 416676.00
(12 rows)
Time: 156.789 ms
3.4 GreenPlum数据库复制表创建实战
3.4.1 创建复制表
fgedudb=> CREATE TABLE fgedu.fgedu_dim_date (
date_id INT,
date_date DATE,
year INT,
quarter INT,
month INT,
day INT,
week INT,
day_of_week INT,
is_weekend BOOLEAN,
holiday_name VARCHAR(50)
) DISTRIBUTED REPLICATED;
CREATE TABLE
# 创建产品维度复制表
fgedudb=> CREATE TABLE fgedu.fgedu_dim_product (
product_id INT,
product_name VARCHAR(100),
category_id INT,
category_name VARCHAR(50),
brand VARCHAR(50),
price NUMERIC(10,2),
status VARCHAR(20),
create_time TIMESTAMP
) DISTRIBUTED REPLICATED;
CREATE TABLE
# 查看复制表信息
fgedudb=> SELECT
n.nspname,
c.relname,
pg_get_table_distributedby(c.oid)
FROM pg_class c
JOIN pg_namespace n ON c.relnamespace = n.oid
WHERE c.relname IN (‘fgedu_dim_date’, ‘fgedu_dim_product’);
nspname | relname | pg_get_table_distributedby
———+—————-+—————————-
fgedu | fgedu_dim_date | DISTRIBUTED REPLICATED
fgedu | fgedu_dim_product | DISTRIBUTED REPLICATED
(2 rows)
3.4.2 复制表数据操作
fgedudb=> INSERT INTO fgedu.fgedu_dim_date
SELECT
(d – ‘2025-01-01’::date)::INT + 1 AS date_id,
d AS date_date,
EXTRACT(YEAR FROM d)::INT AS year,
EXTRACT(QUARTER FROM d)::INT AS quarter,
EXTRACT(MONTH FROM d)::INT AS month,
EXTRACT(DAY FROM d)::INT AS day,
EXTRACT(WEEK FROM d)::INT AS week,
EXTRACT(DOW FROM d)::INT AS day_of_week,
EXTRACT(DOW FROM d) IN (0, 6) AS is_weekend,
NULL AS holiday_name
FROM generate_series(‘2025-01-01’::DATE, ‘2026-12-31’::DATE, ‘1 day’::interval) d;
INSERT 0 731
# 查看数据分布(复制表在所有Segment都有完整数据)
fgedudb=> SELECT
gp_segment_id,
COUNT(*) AS row_count
FROM fgedu.fgedu_dim_date
GROUP BY gp_segment_id
ORDER BY gp_segment_id;
gp_segment_id | row_count
—————+———–
0 | 731
1 | 731
2 | 731
3 | 731
4 | 731
5 | 731
(6 rows)
# 使用复制表进行Join查询(性能更好)
fgedudb=> SELECT
d.month,
COUNT(*) AS order_count,
SUM(o.amount) AS total_amount
FROM fgedu.fgedu_aoco_simple o
JOIN fgedu.fgedu_dim_date d ON date_trunc(‘day’, o.create_time) = d.date_date
GROUP BY d.month
ORDER BY d.month;
month | order_count | total_amount
——-+————-+—————-
1 | 83333 | 416674.00
2 | 83334 | 416676.00
3 | 83333 | 416675.00
4 | 83333 | 416665.00
5 | 83333 | 416666.00
6 | 83334 | 416667.00
7 | 83333 | 416668.00
8 | 83333 | 416669.00
9 | 83334 | 416670.00
10 | 83333 | 416671.00
11 | 83333 | 416672.00
12 | 83334 | 416673.00
(12 rows)
Part04-生产案例与实战讲解
4.1 GreenPlum数据库存储模型性能对比
4.1.1 测试准备
fgedudb=> CREATE TABLE fgedu.fgedu_test_heap (
id INT,
col1 VARCHAR(100),
col2 NUMERIC(18,2),
col3 TIMESTAMP,
col4 TEXT
) DISTRIBUTED BY (id);
CREATE TABLE
fgedudb=> CREATE TABLE fgedu.fgedu_test_ao (
id INT,
col1 VARCHAR(100),
col2 NUMERIC(18,2),
col3 TIMESTAMP,
col4 TEXT
) WITH (
appendonly = true,
compresstype = zlib,
compresslevel = 5
) DISTRIBUTED BY (id);
CREATE TABLE
fgedudb=> CREATE TABLE fgedu.fgedu_test_aoco (
id INT,
col1 VARCHAR(100),
col2 NUMERIC(18,2),
col3 TIMESTAMP,
col4 TEXT
) WITH (
appendonly = true,
orientation = column,
compresstype = zstd,
compresslevel = 6
) DISTRIBUTED BY (id);
CREATE TABLE
# 插入测试数据(100万条)
fgedudb=> INSERT INTO fgedu.fgedu_test_heap
SELECT
generate_series(1, 1000000),
‘value_’ || generate_series(1, 1000000),
(random() * 10000)::NUMERIC(18,2),
NOW() – (random() * 365 || ‘ days’)::interval,
repeat(‘test_text_’, 10);
INSERT 0 1000000
# 复制数据到其他表
fgedudb=> INSERT INTO fgedu.fgedu_test_ao SELECT * FROM fgedu.fgedu_test_heap;
INSERT 0 1000000
fgedudb=> INSERT INTO fgedu.fgedu_test_aoco SELECT * FROM fgedu.fgedu_test_heap;
INSERT 0 1000000
4.1.2 性能对比测试
fgedudb=> SELECT
‘Heap’ AS table_type,
pg_size_pretty(pg_total_relation_size(‘fgedu.fgedu_test_heap’)) AS size
UNION ALL
SELECT
‘AO’,
pg_size_pretty(pg_total_relation_size(‘fgedu.fgedu_test_ao’))
UNION ALL
SELECT
‘AOCO’,
pg_size_pretty(pg_total_relation_size(‘fgedu.fgedu_test_aoco’));
table_type | size
————+———–
Heap | 185 MB
AO | 85 MB
AOCO | 58 MB
(3 rows)
# 插入性能测试
fgedudb=> \timing on
Timing is on.
# 堆表插入
fgedudb=> INSERT INTO fgedu.fgedu_test_heap
SELECT
generate_series(1000001, 1100000),
‘value_’ || generate_series(1000001, 1100000),
(random() * 10000)::NUMERIC(18,2),
NOW() – (random() * 365 || ‘ days’)::interval,
repeat(‘test_text_’, 10);
INSERT 0 100000
Time: 2345.678 ms
# AO表插入
fgedudb=> INSERT INTO fgedu.fgedu_test_ao
SELECT
generate_series(1000001, 1100000),
‘value_’ || generate_series(1000001, 1100000),
(random() * 10000)::NUMERIC(18,2),
NOW() – (random() * 365 || ‘ days’)::interval,
repeat(‘test_text_’, 10);
INSERT 0 100000
Time: 1567.890 ms
# AOCO表插入
fgedudb=> INSERT INTO fgedu.fgedu_test_aoco
SELECT
generate_series(1000001, 1100000),
‘value_’ || generate_series(1000001, 1100000),
(random() * 10000)::NUMERIC(18,2),
NOW() – (random() * 365 || ‘ days’)::interval,
repeat(‘test_text_’, 10);
INSERT 0 100000
Time: 1890.123 ms
# 全列查询性能测试
fgedudb=> SELECT * FROM fgedu.fgedu_test_heap LIMIT 10000;
Time: 123.456 ms
fgedudb=> SELECT * FROM fgedu.fgedu_test_ao LIMIT 10000;
Time: 89.012 ms
fgedudb=> SELECT * FROM fgedu.fgedu_test_aoco LIMIT 10000;
Time: 145.678 ms
# 单列查询性能测试
fgedudb=> SELECT COUNT(DISTINCT col1) FROM fgedu.fgedu_test_heap;
Time: 456.789 ms
fgedudb=> SELECT COUNT(DISTINCT col1) FROM fgedu.fgedu_test_ao;
Time: 345.678 ms
fgedudb=> SELECT COUNT(DISTINCT col1) FROM fgedu.fgedu_test_aoco;
Time: 156.789 ms
# 聚合查询性能测试
fgedudb=> SELECT
date_trunc(‘month’, col3) AS month,
COUNT(*),
SUM(col2)
FROM fgedu.fgedu_test_heap
GROUP BY month
ORDER BY month;
Time: 234.567 ms
fgedudb=> SELECT
date_trunc(‘month’, col3) AS month,
COUNT(*),
SUM(col2)
FROM fgedu.fgedu_test_ao
GROUP BY month
ORDER BY month;
Time: 178.901 ms
fgedudb=> SELECT
date_trunc(‘month’, col3) AS month,
COUNT(*),
SUM(col2)
FROM fgedu.fgedu_test_aoco
GROUP BY month
ORDER BY month;
Time: 89.012 ms
4.1.3 性能对比总结
存储效率:
– AOCO表:最高(58MB)
– AO表:中等(85MB)
– 堆表:最低(185MB)
插入性能:
– AO表:最快(1567ms)
– AOCO表:中等(1890ms)
– 堆表:最慢(2345ms)
全列查询性能:
– AO表:最快(89ms)
– 堆表:中等(123ms)
– AOCO表:最慢(145ms)
单列查询性能:
– AOCO表:最快(156ms)
– AO表:中等(345ms)
– 堆表:最慢(456ms)
聚合查询性能:
– AOCO表:最快(89ms)
– AO表:中等(178ms)
– 堆表:最慢(234ms)
结论:
– OLAP场景推荐使用AOCO表
– 需要更新的表使用堆表
– 维度表使用复制表
– 根据业务场景选择合适的存储模型
4.2 GreenPlum数据库压缩效果测试
4.2.1 压缩对比测试
fgedudb=> CREATE TABLE fgedu.fgedu_compress_none (
id INT,
data VARCHAR(1000)
) WITH (appendonly = true) DISTRIBUTED BY (id);
CREATE TABLE
fgedudb=> CREATE TABLE fgedu.fgedu_compress_zlib3 (
id INT,
data VARCHAR(1000)
) WITH (appendonly = true, compresstype = zlib, compresslevel = 3) DISTRIBUTED BY (id);
CREATE TABLE
fgedudb=> CREATE TABLE fgedu.fgedu_compress_zlib6 (
id INT,
data VARCHAR(1000)
) WITH (appendonly = true, compresstype = zlib, compresslevel = 6) DISTRIBUTED BY (id);
CREATE TABLE
fgedudb=> CREATE TABLE fgedu.fgedu_compress_zlib9 (
id INT,
data VARCHAR(1000)
) WITH (appendonly = true, compresstype = zlib, compresslevel = 9) DISTRIBUTED BY (id);
CREATE TABLE
fgedudb=> CREATE TABLE fgedu.fgedu_compress_zstd3 (
id INT,
data VARCHAR(1000)
) WITH (appendonly = true, compresstype = zstd, compresslevel = 3) DISTRIBUTED BY (id);
CREATE TABLE
fgedudb=> CREATE TABLE fgedu.fgedu_compress_zstd6 (
id INT,
data VARCHAR(1000)
) WITH (appendonly = true, compresstype = zstd, compresslevel = 6) DISTRIBUTED BY (id);
CREATE TABLE
fgedudb=> CREATE TABLE fgedu.fgedu_compress_zstd9 (
id INT,
data VARCHAR(1000)
) WITH (appendonly = true, compresstype = zstd, compresslevel = 9) DISTRIBUTED BY (id);
CREATE TABLE
# 插入测试数据(包含重复数据)
fgedudb=> INSERT INTO fgedu.fgedu_compress_none
SELECT
generate_series(1, 100000),
CASE
WHEN generate_series(1, 100000) % 1000 = 0 THEN ‘pattern_’ || (generate_series(1, 100000) / 1000)
ELSE ‘repeated_pattern’
END;
INSERT 0 100000
# 复制数据到各压缩表
fgedudb=> INSERT INTO fgedu.fgedu_compress_zlib3 SELECT * FROM fgedu.fgedu_compress_none;
INSERT 0 100000
fgedudb=> INSERT INTO fgedu.fgedu_compress_zlib6 SELECT * FROM fgedu.fgedu_compress_none;
INSERT 0 100000
fgedudb=> INSERT INTO fgedu.fgedu_compress_zlib9 SELECT * FROM fgedu.fgedu_compress_none;
INSERT 0 100000
fgedudb=> INSERT INTO fgedu.fgedu_compress_zstd3 SELECT * FROM fgedu.fgedu_compress_none;
INSERT 0 100000
fgedudb=> INSERT INTO fgedu.fgedu_compress_zstd6 SELECT * FROM fgedu.fgedu_compress_none;
INSERT 0 100000
fgedudb=> INSERT INTO fgedu.fgedu_compress_zstd9 SELECT * FROM fgedu.fgedu_compress_none;
INSERT 0 100000
# 查看压缩效果
fgedudb=> SELECT
‘None’ AS compress_type,
pg_size_pretty(pg_total_relation_size(‘fgedu.fgedu_compress_none’)) AS size
UNION ALL
SELECT ‘ZLIB-3’, pg_size_pretty(pg_total_relation_size(‘fgedu.fgedu_compress_zlib3’))
UNION ALL
SELECT ‘ZLIB-6’, pg_size_pretty(pg_total_relation_size(‘fgedu.fgedu_compress_zlib6’))
UNION ALL
SELECT ‘ZLIB-9’, pg_size_pretty(pg_total_relation_size(‘fgedu_compress_zlib9’))
UNION ALL
SELECT ‘ZSTD-3’, pg_size_pretty(pg_total_relation_size(‘fgedu.fgedu_compress_zstd3’))
UNION ALL
SELECT ‘ZSTD-6’, pg_size_pretty(pg_total_relation_size(‘fgedu.fgedu_compress_zstd6’))
UNION ALL
SELECT ‘ZSTD-9’, pg_size_pretty(pg_total_relation_size(‘fgedu.fgedu_compress_zstd9’))
ORDER BY
CASE
WHEN compress_type = ‘None’ THEN 1
WHEN compress_type LIKE ‘ZLIB%’ THEN 2
WHEN compress_type LIKE ‘ZSTD%’ THEN 3
END,
compress_type;
compress_type | size
—————+———
None | 105 MB
ZLIB-3 | 35 MB
ZLIB-6 | 28 MB
ZLIB-9 | 25 MB
ZSTD-3 | 30 MB
ZSTD-6 | 22 MB
ZSTD-9 | 18 MB
(7 rows)
压缩比计算:
– ZLIB-3: 3.0:1
– ZLIB-6: 3.8:1
– ZLIB-9: 4.2:1
– ZSTD-3: 3.5:1
– ZSTD-6: 4.8:1
– ZSTD-9: 5.8:1
4.3 GreenPlum数据库表设计生产案例
4.3.1 电商数据仓库表设计
# ODS层订单表(AO表)
fgedudb=> CREATE TABLE ods.fgedu_orders (
order_id VARCHAR(50),
customer_id VARCHAR(50),
product_id VARCHAR(50),
order_amount DECIMAL(18,2),
order_status VARCHAR(20),
order_time TIMESTAMP,
create_time TIMESTAMP DEFAULT NOW(),
etl_time TIMESTAMP DEFAULT NOW()
) WITH (
appendonly = true,
compresstype = quicklz,
compresslevel = 1
) DISTRIBUTED BY (order_id);
CREATE TABLE
# DWD层订单明细表(AO表)
fgedudb=> CREATE TABLE dwd.fgedu_order_detail (
order_id VARCHAR(50),
customer_id VARCHAR(50),
customer_name VARCHAR(100),
product_id VARCHAR(50),
product_name VARCHAR(100),
category_id VARCHAR(50),
category_name VARCHAR(50),
order_amount DECIMAL(18,2),
order_status VARCHAR(20),
order_time TIMESTAMP,
province VARCHAR(50),
city VARCHAR(50),
create_time TIMESTAMP,
etl_time TIMESTAMP DEFAULT NOW()
) WITH (
appendonly = true,
compresstype = zlib,
compresslevel = 5
) DISTRIBUTED BY (order_id);
CREATE TABLE
# DWS层销售汇总表(AOCO表)
fgedudb=> CREATE TABLE dws.fgedu_sales_summary (
date_id INT,
province VARCHAR(50),
city VARCHAR(50),
category_id VARCHAR(50),
category_name VARCHAR(50),
order_count BIGINT,
order_amount DECIMAL(18,2),
customer_count BIGINT,
create_time TIMESTAMP DEFAULT NOW()
) WITH (
appendonly = true,
orientation = column,
compresstype = zstd,
compresslevel = 6
) DISTRIBUTED BY (date_id, province);
CREATE TABLE
# 维度表(复制表)
fgedudb=> CREATE TABLE dim.fgedu_dim_date (
date_id INT,
date_date DATE,
year INT,
quarter INT,
month INT,
day INT,
week INT,
day_of_week INT,
is_weekend BOOLEAN,
is_holiday BOOLEAN,
holiday_name VARCHAR(50)
) DISTRIBUTED REPLICATED;
CREATE TABLE
fgedudb=> CREATE TABLE dim.fgedu_dim_product (
product_id VARCHAR(50),
product_name VARCHAR(100),
category_id VARCHAR(50),
category_name VARCHAR(50),
brand VARCHAR(50),
price DECIMAL(10,2),
status VARCHAR(20),
create_time TIMESTAMP
) DISTRIBUTED REPLICATED;
CREATE TABLE
fgedudb=> CREATE TABLE dim.fgedu_dim_customer (
customer_id VARCHAR(50),
customer_name VARCHAR(100),
gender VARCHAR(10),
age INT,
province VARCHAR(50),
city VARCHAR(50),
level VARCHAR(20),
register_date DATE,
status VARCHAR(20)
) DISTRIBUTED REPLICATED;
CREATE TABLE
Part05-风哥经验总结与分享
5.1 GreenPlum数据库表设计最佳实践
5.1.1 表设计最佳实践
1. 存储模型选择
– 频繁更新的表 → 堆表
– 只追加的表 → AO表
– 分析查询表 → AOCO表
– 小的维度表 → 复制表
2. 压缩策略
– 热数据 → 低压缩级别(1-3)
– 温数据 → 中等压缩级别(4-6)
– 冷数据 → 高压缩级别(7-9)
– 重复数据 → RLE压缩
– 文本数据 → ZSTD压缩
3. 数据类型选择
– 选择最小的适合的数据类型
– 避免使用过大的数据类型
– 使用合适的日期时间类型
– 避免使用TEXT类型存储短字符串
4. 表设计规范
– 所有表都要有分布键
– 避免数据倾斜
– 合理使用分区
– 避免过多索引
– 定期更新统计信息
5.1.2 表维护最佳实践
- 定期ANALYZE更新统计信息
- 堆表定期VACUUM清理
- AO表定期VACUUM回收空间
- 监控表空间使用情况
- 定期检查表数据分布
- 及时清理不再需要的表
5.2 GreenPlum数据库表设计检查清单
创建表前检查:
[ ] 存储模型选择是否合适
[ ] 分布键选择是否合理
[ ] 是否需要分区
[ ] 数据类型选择是否合适
[ ] 压缩策略是否确定
[ ] 表名是否符合规范
创建表后检查:
[ ] 表是否创建成功
[ ] 分布键是否正确
[ ] 压缩配置是否正确
[ ] 表空间是否正确
[ ] 权限是否正确配置
[ ] 注释是否添加
数据加载后检查:
[ ] 数据是否完整
[ ] 数据分布是否均匀
[ ] 压缩效果是否符合预期
[ ] 统计信息是否更新
[ ] 查询性能是否正常
日常维护检查:
[ ] 统计信息是否定期更新
[ ] 表空间使用是否正常
[ ] 表是否需要VACUUM
[ ] 索引是否需要重建
[ ] 是否有不再需要的表
性能优化检查:
[ ] 查询是否有倾斜
[ ] 表是否需要重新分布
[ ] 分区是否合理
[ ] 压缩策略是否需要调整
[ ] 索引是否需要优化
5.3 GreenPlum数据库表管理脚本
5.3.1 表信息查询脚本
# gp_table_info.sh
# from:www.itpux.com.qq113257174.wx:itpux-com
# web: http://www.fgedu.net.cn
echo “==========================================”
echo “GreenPlum表信息报告”
echo “生成时间: $(date)”
echo “==========================================”
# 按Schema统计表数量
echo “”
echo “=== 按Schema统计表数量 ===”
psql -d fgedudb -c ”
SELECT
schemaname,
COUNT(*) AS table_count
FROM pg_tables
WHERE schemaname NOT IN (‘pg_catalog’, ‘information_schema’, ‘gp_toolkit’)
GROUP BY schemaname
ORDER BY table_count DESC;
”
# 表大小Top10
echo “”
echo “=== 表大小Top10 ===”
psql -d fgedudb -c ”
SELECT
schemaname || ‘.’ || tablename AS table_name,
pg_size_pretty(pg_total_relation_size(schemaname || ‘.’ || tablename)) AS table_size
FROM pg_tables
WHERE schemaname NOT IN (‘pg_catalog’, ‘information_schema’, ‘gp_toolkit’)
ORDER BY pg_total_relation_size(schemaname || ‘.’ || tablename) DESC
LIMIT 10;
”
# 表存储类型统计
echo “”
echo “=== 表存储类型统计 ===”
psql -d fgedudb -c ”
SELECT
CASE
WHEN c.relstorage = ‘h’ THEN ‘Heap’
WHEN c.relstorage = ‘a’ THEN ‘AO’
WHEN c.relstorage = ‘c’ THEN ‘AOCO’
ELSE ‘Other’
END AS storage_type,
COUNT(*) AS table_count,
pg_size_pretty(SUM(pg_total_relation_size(c.oid))::bigint) AS total_size
FROM pg_class c
JOIN pg_namespace n ON c.relnamespace = n.oid
WHERE c.relkind = ‘r’
AND n.nspname NOT IN (‘pg_catalog’, ‘information_schema’, ‘gp_toolkit’)
GROUP BY
CASE
WHEN c.relstorage = ‘h’ THEN ‘Heap’
WHEN c.relstorage = ‘a’ THEN ‘AO’
WHEN c.relstorage = ‘c’ THEN ‘AOCO’
ELSE ‘Other’
END
ORDER BY table_count DESC;
”
echo “”
echo “==========================================”
echo “报告生成完成”
echo “==========================================”
5.3.2 数据分布检查脚本
# gp_distribution_check.sh
# from:www.itpux.com.qq113257174.wx:itpux-com
# web: http://www.fgedu.net.cn
echo “==========================================”
echo “GreenPlum数据分布检查”
echo “检查时间: $(date)”
echo “==========================================”
# 需要检查的表
TABLES=”
fgedu.fgedu_orders
fgedu.fgedu_customers
fgedu.fgedu_products
”
for table in $TABLES; do
echo “”
echo “=== 检查表: $table ===”
psql -d fgedudb -c ”
SELECT
gp_segment_id,
COUNT(*) AS row_count,
ROUND(100.0 * COUNT(*) / SUM(COUNT(*)) OVER (), 2) AS percentage
FROM $table
GROUP BY gp_segment_id
ORDER BY gp_segment_id;
”
done
echo “”
echo “==========================================”
echo “检查完成”
echo “==========================================”
更多视频教程www.fgedu.net.cn
本文由风哥教程整理发布,仅用于学习测试使用,转载注明出处:http://www.fgedu.net.cn/10327.html
