PostgreSQL 分区表的原理、实践与 MySQL 对比
一、背景
需要对AI调用进行记录,并对失败过高的情况进行告警。但是目前每天的AI调用量在百万级,对应的数据量也在百万级,单表存储显然是不可靠的。单表过大带来的问题包括:
- 查询、插入、更新性能下降;
- 索引膨胀,维护困难;
- 清理历史数据成本高;
- 统计与聚合操作耗时。
在这种情况下,表分区(Partitioning) 是一种非常有效的优化手段。
二、什么是分区表
分区表(Partitioned Table)是一个 逻辑表,数据按某种规则(范围、列表、哈希)分布在多个物理子表中(称为分区 / Partition)。
应用程序仍然对“主表”执行查询或写入,数据库内核自动将操作路由到对应的分区。
🔹 举个例子
假设我们有一个 AI 模型调用日志表 ai_call_record,每天产生数百万条记录。
我们可以根据时间字段 started_at 按天进行分区:
1 | CREATE TABLE ai_call_record ( |
PostgreSQL 会根据 started_at 的值自动把数据路由到对应的子表:
1 | CREATE TABLE ai_call_record_20251113 |
当插入 2025-11-13 10:00:00 的数据时,会自动进入该分区表。
三、PostgreSQL 分区类型
PostgreSQL 从 v10 开始原生支持表分区(之前依靠继承 + 触发器实现)。
常见的分区类型如下:
| 类型 | 划分方式 | 适用场景 | 示例 |
|---|---|---|---|
| 范围分区(RANGE) | 按连续区间划分 | 日志、订单等时间序列数据 | 按天或按月分区 |
| 列表分区(LIST) | 按离散值划分 | 地区、租户、业务类型 | 按国家或渠道分区 |
| 哈希分区(HASH) | 按哈希余数均匀划分 | 没有自然范围、需要分散写入的数据 | 按用户 ID 分为 8 个分区 |
范围分区最适合本文的调用日志场景,因为查询和清理通常都以时间为边界。列表分区要求提前明确值集合;哈希分区能让数据分布更均匀,但不适合按时间快速删除历史数据。
四、PostgreSQL 分区表的优势
✅ 1. 自动路由与查询优化
当你执行:SELECT * FROM ai_call_record WHERE started_at >= now() - interval '1 day';
PostgreSQL 的 分区裁剪(Partition Pruning) 能自动只扫描符合条件的分区,大幅减少 I/O。
✅ 2. 高效的归档与清理
删除历史数据非常简单:DROP TABLE ai_call_record_20251001;
比传统 DELETE 快几个数量级。
✅ 3. 独立索引与并行查询
每个分区都有自己的索引,查询时只访问相关索引;
分区之间还能并行扫描,提高吞吐。
✅ 4. 与原表操作几乎一致INSERT/UPDATE/SELECT/DELETE 都不需要改应用逻辑,完全透明。
只是 DDL 管理上稍微复杂(需要自动建分区任务,如 pg_cron)。
五、MySQL 的分区表机制
MySQL 也支持分区(从 5.1 起),主要由 InnoDB 实现。
两者在设计思想相似,但实现方式有明显差异:
| 场景 | PostgreSQL 分区表 | MySQL 分区表 |
|---|---|---|
| 单分区写入 | 快,近似普通表 | 类似 |
| 分区裁剪查询 | 精准裁剪,性能稳定 | 条件匹配不严格会全表扫描 |
| 删除历史分区 | DROP TABLE 秒级完成 | ALTER TABLE DROP PARTITION 也快,但锁表风险高 |
| 索引管理 | 独立索引,灵活 | 全局索引支持有限 |
| 自动化管理 | 可借助 pg_cron 或 pg_partman | 无官方自动分区管理工具 |
| 并行查询 | ✅ 支持多分区并行 | ❌ 主要单线程 |
六、实战案例:AI 调用日志按天分区
1️⃣ 分区表定义
1 | CREATE TABLE ai_call_record ( |
2️⃣ 自动分区创建函数(每日)
1 | CREATE OR REPLACE FUNCTION create_ai_call_partitions() |
3️⃣ 删除过期分区DROP TABLE ai_call_record_20251001;
🚀 清理历史数据只需秒级,性能极高。
七、性能对比:PostgreSQL vs MySQL
| 场景 | PostgreSQL 分区表 | MySQL 分区表 |
|---|---|---|
| 单分区写入 | 快,近似普通表 | 类似 |
| 分区裁剪查询 | 精准裁剪,性能稳定 | 条件匹配不严格会全表扫描 |
| 删除历史分区 | DROP TABLE 秒级完成 | ALTER TABLE DROP PARTITION 也快,但锁表风险高 |
| 索引管理 | 独立索引,灵活 | 全局索引支持有限 |
| 自动化管理 | 可借助 pg_cron 或 pg_partman | 无官方自动分区管理工具 |
| 并行查询 | ✅ 支持多分区并行 | ❌ 主要单线程 |
结论:
- PostgreSQL 的分区表更接近“多表聚合”的设计,灵活、易扩展。
- MySQL 的分区是“逻辑分片”,不如 PostgreSQL 强大,但轻量。
- 如果你有时间序列、日志型、归档型数据,PostgreSQL 分区是更优方案。
八、最佳实践建议
- 选择合适的分区键
常见为时间戳字段(如 created_at、started_at)。 - 提前创建分区
使用pg_cron或pg_partman定期自动创建未来分区。 - 清理过期分区
定期删除旧分区,避免表膨胀。 - 索引策略
仅对查询必要的字段建索引,避免每个分区维护大量冗余索引。 - 监控与维护
使用pg_stat_user_tables、pg_inherits监控分区数量和大小。
九、总结
| 对比项 | PostgreSQL | MySQL |
|---|---|---|
| 分区灵活性 | ✅ 高 | ⚠️ 一般 |
| 自动化支持 | ✅ pg_cron / pg_partman | ❌ 需手工 |
| 大数据量表现 | ✅ 稳定 | ⚠️ 易退化 |
| 清理历史数据 | ✅ 秒级 DROP | ✅ 快,但锁表 |
| 查询优化 | ✅ 分区裁剪智能 | ⚠️ 条件匹配要求高 |
✅ 一句话总结:
PostgreSQL 的分区表是生产级别的“时间序列 + 大数据归档”解决方案,
而 MySQL 的分区功能更像是一种轻量级的逻辑分割。
📘 推荐扩展阅读
- 标题: PostgreSQL 分区表的原理、实践与 MySQL 对比
- 作者: Elvis
- 创建于 : 2025-11-14 10:00:00
- 更新于 : 2026-07-25 09:07:17
- 链接: https://qianwj.github.io/2025/11/14/postgres_partition/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。