KingbaseES 在存储管理层面构建了一套完整的技术体系,涵盖超大字段处理、I/O 性能分层以及内存大页优化三大核心模块。下面从原理到实践,逐一拆解这三个关键机制。
一、TOAST 技术:超大字段的"压缩与分片"之道
1.1 为什么需要 TOAST?
KingbaseES 以**页(Page)**作为数据文件存储的基本单位,默认大小为 8KB,且严格禁止单行数据跨页存储。这意味着页大小就是行大小的硬上限。
当业务中出现 TEXT、BYTEA、JSONB 等大字段时,若不加处理,一行数据极易突破这一上限。TOAST(The Oversized-Attribute Storage Technique,超大属性存储技术)正是为此而生——通过压缩 + 切片行外存储两种手段,将超大字段"瘦身"后安全落盘,且对用户完全透明。
1.2 TOAST 的触发机制
| 阶段 | 触发条件 | 行为 |
|---|---|---|
| 压缩 | 字段数据 > 2KB(TOAST_TUPLE_THRESHOLD) | 优先对数据进行压缩 |
| 行外存储 | 压缩后仍 > 2KB | 切片写入独立 TOAST 表,原字段替换为指针 |
| 不处理 | 数据 ≤ 2KB | 直接存储在主表行内 |
每张主表都有一张唯一关联的 TOAST 表,命名规则为
pg_toast.pg_toast_<主表OID>,结构固定为三列:chunk_id(块标识)、chunk_seq(块序号)、chunk_data(实际数据)。
1.3 四种 TOAST 存储策略
PLAIN → 禁止压缩 + 禁止行外存储(适用于 INTEGER 等定长类型)
EXTENDED → 允许压缩 + 允许行外存储(TEXT 等变长类型的默认策略)
EXTERNAL → 禁止压缩 + 允许行外存储(牺牲空间换取随机访问速度)
MAIN → 允许压缩 + 禁止行外存储(尽量保留在主表行内)
查看与修改策略的方式:
-- 查看字段存储策略
\d+ tosttest
-- 修改为 EXTERNAL(禁止压缩,允许行外存储)
ALTER TABLE tosttest ALTER COLUMN name SET STORAGE EXTERNAL;
注意:修改 TOAST 策略不会影响已有数据的存储方式,仅对后续写入生效。
1.4 实验验证:EXTENDED vs EXTERNAL 的差异
以下实验直观展示了两种策略在行外存储时的本质区别:
EXTENDED 策略(先压缩,再行外存储):
name 字段长度达到 327,680 字节时,TOAST 表仅产生 2 行:
chunk_seq=0 → 1988 字节
chunk_seq=1 → 1781 字节
合计约 3.7KB(原始数据经压缩后大幅缩小)
EXTERNAL 策略(禁止压缩,直接行外存储):
name 字段长度为 5,120 字节时,TOAST 表产生 3 行:
chunk_seq=0 → 1988 字节
chunk_seq=1 → 1988 字节
chunk_seq=2 → 1144 字节
合计 = 5120 字节(与原始数据完全一致,未压缩)
核心结论: EXTENDED 策略下,压缩优先于行外存储;一旦数据(压缩后)超过 2KB,无论哪种策略,均会触发行外存储。
二、逻辑读与物理读:I/O 性能的分层理解
2.1 KingbaseES 的内存区域划分
KingbaseES 的数据访问路径涉及三类内存区域,理解它们是分析 I/O 性能的基础:
| 内存区域 | 作用 | 可见范围 |
|---|---|---|
shared_buffers | 共享数据库块缓冲,所有关系表的读写必经之路 | 所有会话共享 |
temp_buffers | 临时表专用缓冲 | 仅当前会话可见 |
work_mem / maintenance_work_mem | 排序、Hash 等操作的工作内存,不足时溢出到临时文件 | 仅当前会话可见 |
2.2 四种数据访问方式
Hit(逻辑读命中) → 直接在 shared/local buffer 中找到数据,零磁盘 IO
Dirty(脏写) → 数据写入内存缓冲区,尚未落盘
Read(物理读) → 内存未命中,从操作系统缓存或磁盘读入内存
Written(物理写) → 从内存缓冲区写出到操作系统缓存或磁盘文件
2.3 通过 sys_stat_statements 量化 I/O
sys_stat_statements 视图是诊断 SQL 性能的核心工具,关键字段含义如下:
| 字段 | 含义 | I/O 类型 |
|---|---|---|
shared_blks_hit | shared_buffer 命中块数 | 逻辑读(无 IO) |
shared_blks_read | 从 OS 缓存/磁盘读入 shared_buffer 的块数 | 物理读 |
shared_blks_written | 从 shared_buffer 写出的块数 | 物理写 |
local_blks_read | 临时表缓冲未命中,从 OS 读入的块数 | 物理读 |
temp_blks_read | 从临时文件读入 work_mem 的块数 | 物理读(排序溢出) |
temp_blks_written | 从 work_mem 写入临时文件的块数 | 物理写(排序溢出) |
与 Oracle 的重要区别: KingbaseES 的
read/written统计包含操作系统缓存层,即使数据已在 OS Page Cache 中命中,也会被计入物理读,而 Oracle 的物理读统计不考虑 OS 缓存层。
2.4 性能诊断思路
逻辑读高(shared_blks_hit 大) → 缓存利用率好,性能优
物理读高(shared_blks_read 大) → 缓存命中率低,考虑增大 shared_buffers 或优化索引
temp_blks 高 → 排序/Hash 内存不足,考虑增大 work_mem
KWR 报告中的 TOP SQL 模块可直接定位高 I/O 消耗的 SQL 语句,是日常巡检的重要入口。
三、Hugepage 配置:锁定共享内存,告别 Swap
3.1 为什么要配置 Hugepage?
Linux 默认以 4KB 为单位管理内存,维护虚拟地址到物理地址映射的 Page Table 会随内存增大而急剧膨胀。对于 KingbaseES 这类需要大 shared_buffers 的数据库,开启 标准 Hugepage(2MB/页) 带来三重收益:
- 降低 Page Table 开销:同等内存下,Hugepage 的页表条目数仅为普通页的 1/512
- 锁定物理内存:Hugepage 预分配且不可被 Swap 换出,避免
shared_buffers被置换到磁盘 - 提升 TLB 命中率:更大的页面覆盖更多地址空间,减少 TLB Miss
透明大页(Transparent Hugepage)与标准大页同时存在时可能引发性能问题,强烈建议禁用透明大页。
3.2 标准 Hugepage 配置步骤
第一步:计算所需 Hugepage 数量
# 获取数据库主进程 PID
head -1 $KINGBASE_DATA/kingbase.pid
# 计算数据库共享内存占用(单位 KB)
pmap <PID> | awk '/rw-s/ && /zero/'
# 示例输出:400800K
# 查看系统 Hugepage 大小
grep ^Hugepagesize /proc/meminfo
# 示例输出:Hugepagesize: 2048 kB
# 计算所需页数:400800 / 2048 ≈ 196 页
第二步:分配并持久化 Hugepage
# 临时生效
sysctl -w vm.nr_hugepages=196
# 持久化配置
echo "vm.nr_hugepages = 196" >> /etc/sysctl.conf
sysctl -p
第三步:验证分配结果
cat /proc/meminfo | grep -i huge
# 关注:HugePages_Total=196,HugePages_Free=196
3.3 禁用透明大页
在 /etc/rc.local 中添加以下内容并重启:
if test -f /sys/kernel/mm/transparent_hugepage/enabled; then
echo never > /sys/kernel/mm/transparent_hugepage/enabled
fi
if test -f /sys/kernel/mm/transparent_hugepage/defrag; then
echo never > /sys/kernel/mm/transparent_hugepage/defrag
fi
3.4 配置数据库参数
在 kingbase.conf 中设置:
huge_pages = try # 推荐:优先使用大页,不足时自动回退到普通页
# huge_pages = on # 强制使用大页,大页不足则启动失败(生产环境谨慎使用)
验证是否生效: 重启数据库后,执行 pmap <新PID> | awk '/rw-s/ && /zero/',若无输出,则说明 Hugepage 已成功接管 shared_buffers 内存区域。
总结:三大机制的协同价值
这三项技术共同构成了 KingbaseES 存储管理的核心骨架:
| 技术 | 解决的核心问题 | 关键参数/阈值 |
|---|---|---|
| TOAST | 超大字段突破 8KB 页限制 | TOAST_TUPLE_THRESHOLD = 2KB |
| 逻辑读/物理读 | 量化 SQL 的 I/O 消耗,定位性能瓶颈 | sys_stat_statements 视图 |
| Hugepage | 锁定 shared_buffers,消除 Swap 风险 | huge_pages = try |
三者从数据存储形态、I/O 行为分析、内存资源管理三个维度相互补充——TOAST 决定数据如何存,逻辑/物理读决定如何量化访问代价,Hugepage 则保障内存访问的稳定性与效率。掌握这三个机制,是深入理解 KingbaseES 性能调优的重要基础。