AO表和AOCO表的适用场景和选型
AO表和AOCO表的适用场景和选型
前言
在 PostgreSQL(Greenplum / GPDB) 体系里,AO(Append-Only Row) 和 AOCO(Append-Only Column) 是两种典型的**Append-Only **存储格式,主要用于 数据仓库 / OLAP 场景,区别核心在:
👉 AO = 行存追加表
👉 AOCO = 列存追加表
它们都不是 PostgreSQL 原生 Heap 表,而是 Greenplum 的高性能分析型存储。
下面从 原理、适用场景、性能对比、选型建议 给你系统讲清。
一、存储结构本质区别
| 类型 | 存储方式 | 数据组织 |
|---|---|---|
| Heap(普通表) | 行存 + 可更新 | MVCC |
| AO | 行存 + 追加写 | 不支持 UPDATE 原地 |
| AOCO | 列存 + 追加写 | 每列独立存储 |
AO(Append Only Row)
一行完整写在一起
[ row1 ]
[ row2 ]
[ row3 ]
AOCO(Append Only Column)
col1: v1 v2 v3 v4
col2: v1 v2 v3 v4
col3: v1 v2 v3 v4
适用场景详解
1. Heap表(PostgreSQL继承的行存)
- 核心定位:OLTP友好型、维度表、小表。
- 数据量:百万级以下,或单表<10GB。官方建议小表(<100万条)使用Heap。
- 操作特征:频繁UPDATE/DELETE,大量点查询(带索引),并发小事务。
- 典型对象:数据仓库中的维度表(用户表、产品表)、配置表、码表。
- 为什么用它:MVCC机制成熟,行级锁开销可控,VACUUM清理速度比AO表快。
2. AORO表(Append-Optimized Row-Oriented)
- 核心定位:大表的明细存储,写入密集且修改极少。
- 数据量:千万级~亿级以上。
- 操作特征:批量INSERT(ETL)、几乎无UPDATE、查询时需要读取大部分列(如SELECT * 明细查询)。
- 典型对象:日志原始表、流水表、审计表。
- 为什么用它:相比Heap,存储更紧凑、无空洞;相比AOCO,不需要列存重组开销。
- 重要限制:不支持列级压缩,压缩算法仅zlib(开源版),ADD COLUMN会重写表(耗时极长)。
3. AOCO表(Append-Optimized Column-Oriented)
- 核心定位:数仓核心事实表,OLAP分析场景的王者。
- 数据量:TB级,宽表(几十~几百列)。
- 操作特征:查询只取少数列(聚合、过滤),极少点查询;写入以批量追加为主。
- 典型对象:交易事实表、点击流分析表、用户行为宽表。
- 为什么用它:列级压缩(ZSTD可省75%空间)、只读所需列大幅降低I/O、支持向量化执行加速。
- 特殊优势:DDL灵活,ADD COLUMN(DEFAULT NULL)不重写表,瞬间完成。
表性能全面对比
以下对比基于相同硬件环境、海量数据(亿级)的典型行为:
| 维度 | Heap表 | AORO表 | AOCO表 | 关键结论 |
|---|---|---|---|---|
| 查询性能 | 差(扫描大表时I/O巨大) | 中等(需读整行) | 极优(仅读投影列) | AOCO分析快,可快数倍至数十倍(取决列选择率) |
| 压缩比 | 无(或页级压缩弱) | 中等(整表zlib) | 极高(ZSTD/LZ4/RLE) | AOCO压缩率比AORO高60%~75% |
| 写入速度 | 慢(索引维护、WAL) | 极快(仅追加) | 快(列存组装有开销) | AORO批量导入最快,AOCO次之,Heap最慢 |
| UPDATE/DELETE | 支持好(适合单行) | 极差(标记删除,VACUUM极慢) | 极差(同左) | 大表频繁更新禁止用AO |
| 点查询(带索引) | 最优 | 差(变长块寻址慢) | 差(列重组开销大) | 点查询必须用Heap |
| DDL灵活性 | ADD COLUMN慢(需重写) | ADD COLUMN 极慢(必重写) | 最优(不重写) | AOCO修改Schema几乎零成本 |
| 内存消耗 | 低(共享缓冲池) | 低(不经过共享缓冲) | 高(每分区每列有buffer) | AOCO blocksize不可过大,否则内存爆炸 |
| 维护成本(VACUUM) | 快 | 慢(需重构bitmap) | 慢(同左) | 有大量更新的AO表维护成本远高于Heap |
| 逻辑备份 | 不支持增量 | 支持增量 | 支持增量 | 大表用AO便于备份策略 |
二、适用业务场景(最重要)
⭐ AO(行存)适合
1.宽表 + 经常查询整行
典型:
- 明细表全量导出
- 日志
- 订单明细
- 用户行为记录
查询:
👉 需要整行数据
👉 行存读取更快
select * from fact_order where id=?
2.中等列数(<100列)
列太多 AOCO更合适。
3.写入为主 + 查询不挑列
AO写入效率高,简单。
4.需要较频繁 UPDATE / DELETE(但仍不推荐)
AO支持但成本比Heap高。
⭐ AOCO(列存)适合(数据仓库王者)
1.超大表(TB / PB级)
列存压缩极高。
2.宽表(100+列)
典型:
用户画像
标签表
指标宽表
风控特征表
3️.查询只访问少量列 ⭐⭐⭐
OLAP经典:
select sum(amount)
from fact_order
where date between ...
只扫 2列:
date
amount
👉 行存必须读整行
👉 列存只读相关列
4.聚合分析极多
count
sum
avg
group by
列存CPU缓存友好。
5.高压缩需求
- AOCO支持:
- RLE
- ZLIB
- ZSTD
压缩率常见:
| 数据类型 | 压缩率 |
| ----- | ----- |
| 数值 | 5~20倍 |
| 低基数字段 | 50倍+ |
| 枚举 | 100倍 |
三、性能对比(核心结论)
假设:
表 100列
查询2列
1.扫描性能
| 查询类型 | AO | AOCO |
|---|---|---|
| select * | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
| 读少量列 | ⭐⭐ | ⭐⭐⭐⭐⭐ |
| 聚合 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| 宽表扫描 | ⭐⭐ | ⭐⭐⭐⭐⭐ |
结论:
- 👉 全列读取:AO快
- 👉 部分列:AOCO碾压
2️.存储空间
| 表类型 | 空间 |
|---|---|
| Heap | 100% |
| AO | 60~80% |
| AOCO | 10~30% |
3.写入性能
| 操作 | AO | AOCO |
|---|---|---|
| 批量INSERT | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| 单行INSERT | ⭐⭐⭐ | ⭐⭐ |
| UPDATE | ⭐⭐ | ⭐ |
| DELETE | ⭐⭐ | ⭐ |
列存写入成本更高。
4.更新性能(关键)
Append Only 本质:
- 👉 UPDATE = delete + insert
但 AOCO更重: - 需要重写多列文件
- block级
结论:
| 更新频率 | 推荐 |
| ---- | ---- |
| 高 | Heap |
| 中 | AO |
| 低 | AOCO |
5.CPU效率
AOCO更高:
原因:
- 向量化
- cache友好
- 列压缩减少IO
OLAP性能常见: - 👉 AOCO 比 AO 快 3~20倍
四、真实数据仓库选型经验(非常重要)
行业经验:
| 表类型 | 推荐存储 |
|---|---|
| 事实表(超大) | AOCO |
| 宽表 | AOCO |
| 标签表 | AOCO |
| 报表聚合结果 | AOCO |
| 日志 | AO |
| 明细导出表 | AO |
| 临时ETL表 | AO |
| 高频更新表 | Heap |
五、典型性能对比案例(真实)
数据:
10亿行
120列
查询3列
| 表类型 | 查询时间 |
|---|---|
| Heap | 120s |
| AO | 95s |
| AOCO | 6s |
六、压缩能力对比
| 类型 | 压缩 | 可选 |
|---|---|---|
| AO | 行压缩 | yes |
| AOCO | 列压缩 | 非常强 |
AOCO支持:
ENCODING (compresstype=zstd)
ENCODING (compresstype=rle)
ENCODING (compresstype=zlib)
七、Greenplum官方推荐(核心)
Greenplum设计理念:
- 👉 数据仓库默认用 AOCO
只有以下情况不用:
- 高频更新
- 经常 select *
- 小表
八、创建语法示例
AO
CREATE TABLE t1(...)
WITH (appendonly=true, orientation=row);
AOCO
CREATE TABLE t2(...)
WITH (appendonly=true, orientation=column);
带压缩:
WITH (
appendonly=true,
orientation=column,
compresstype=zstd,
compresslevel=5
);
九、终极选型决策图(实战)
问自己4个问题:
Q1 数据量大吗?
是 → AOCO
Q2 查询只用部分列吗?
是 → AOCO
Q3 表是否宽?
50列 → AOCO
Q4 是否频繁更新?
是 → Heap / AO
十、一句话总结(架构级)
👉 AO = OLAP行存
👉 AOCO = OLAP列存(主流)
👉 AOCO = 数据仓库默认选择
👉 AO = 过渡方案
👉 Heap = 事务系统
建议优先:
大事实表 → AOCO + ZSTD
宽表 → AOCO + RLE
ETL中间表 → AO
高并发更新 → Heap
十一、查看数据库是以增删改查那种业务场景为主
在 Greenplum 里,系统不会直接告诉你“这个表是查询为主还是增删改为主”,
但可以通过 系统统计信息 + 访问模式分析 很准确判断。
核心思路:
- 👉 看 读写操作比例
- 👉 看 扫描方式
- 👉 看 更新膨胀情况
下面给你 DBA 实战级判断方法。
1.查看表读写次数(最关键指标)
SELECT
schemaname,
relname AS table_name,
seq_scan, -- 全表扫描次数
idx_scan, -- 索引扫描次数
n_tup_ins, -- 插入行数
n_tup_upd, -- 更新行数
n_tup_del, -- 删除行数,
n_live_tup, -- 当前活跃行
n_dead_tup -- 死行(更新删除产生)
FROM pg_stat_user_tables
ORDER BY (n_tup_ins + n_tup_upd + n_tup_del) DESC;
2.企业级自动判断脚本(推荐)
SELECT
schemaname,
relname,
seq_scan + idx_scan AS reads,
n_tup_ins + n_tup_upd + n_tup_del AS writes,
CASE
WHEN (seq_scan + idx_scan) > 10*(n_tup_ins + n_tup_upd + n_tup_del)
THEN '查询为主'
WHEN (n_tup_ins + n_tup_upd + n_tup_del) > (seq_scan + idx_scan)
THEN '写入为主'
ELSE '混合'
END AS workload_type
FROM pg_stat_user_tables
ORDER BY reads DESC;
同时查询表的大小
SELECT
schemaname,
relname,
seq_scan + idx_scan AS reads,
n_tup_ins + n_tup_upd + n_tup_del AS writes,
CASE
WHEN (seq_scan + idx_scan) > 10 * (n_tup_ins + n_tup_upd + n_tup_del)
THEN '查询为主'
WHEN (n_tup_ins + n_tup_upd + n_tup_del) > (seq_scan + idx_scan)
THEN '写入为主'
ELSE '混合'
END AS workload_type,
pg_size_pretty(pg_total_relation_size(relid)) AS table_size,
pg_total_relation_size(relid) / (1024 * 1024 * 1024) AS table_size_gb -- 转换成 GB
FROM pg_stat_user_tables
ORDER BY reads DESC;
直接给你存储选型决策
| 表类型 | 存储建议 |
|---|---|
| 查询为主 | AOCO |
| 写入为主 | Heap |
| 批量写入 | AO |
| 更新频繁 | Heap |
| 宽表分析 | AOCO |

浙公网安备 33010602011771号