【金仓数据库征文】金仓 KES 性能调优实录,在鲲鹏加麒麟 V10 上从 KWR 报告一路做到 SQL 优化
测试环境为鲲鹏 aarch64、银河麒麟高级服务器 V10(Tercel)、KingbaseES V009R001C010 企业版(180 天授权)。文中所有压测与 SQL 实验数据均为本机真实采集,附录给了完整的复现命令。
先把结论放在前面。同一台机器、同一份数据、同样的压测命令,只改了十来个内存和 WAL 参数,4 并发只读压测的 TPS 中位数从 54,029 涨到 70,541(各采样 5 次取中位数),提升约 30.6%。SQL 层面更夸张一点,一条主键精确查询,加索引之前 94ms,加完 0.034ms,差了大约 2700 倍。但同一张表上另一条命中 20% 行的范围查询,索引建了等于白建,优化器看都不看一眼,照样全表扫描。
这几个数字放在一起,差不多就是这篇文章想讲的事。调优不是玄学,也不是无脑加索引、无脑堆硬件,它更像是先搞清楚数据库到底在忙什么,再把资源花在刀刃上。
做这次实验的起因很简单。手上正好有一台鲲鹏测试机,装的是银河麒麟 V10,又申请到了金仓 KES V9 的企业版授权,一套很典型的信创组合。平时总能听到一种说法,「国产数据库性能不行」。但我一直怀疑,这口锅有相当一部分要默认参数来背。数据库出厂配置是面向通用场景的,128MB 的 shared_buffers 放在今天任何一台服务器上都显得寒酸,更不会针对鲲鹏这种 ARM 平台做什么适配。默认参数直接上线,跑不快,然后得出结论说产品不行,这个链条其实换成哪家数据库都逃不掉。
所以我想亲手验证一件事,同样的负载,经过一轮有章法的调优,这套全栈国产组合到底能压出什么样的表现。重点不在最后那个数字,在方法,先用 KWR 报告把瓶颈找准,再动手,而不是拍脑袋调参。

整体思路。基线压测、KWR 诊断、参数与 SQL 调优、复测对比,一个闭环,文中每个数字都有对应的截图或报告佐证。
一、测试环境
配置相当入门,4 核鲲鹏,7 个多 G 内存。如果你手头的信创环境也是这种规格,后面的参数基本可以直接抄。
| 项 | 配置 |
|---|---|
| CPU | HiSilicon ARMv8(aarch64),4 核 @ 2.9GHz,单 NUMA 节点 |
| 内存 | 7314 MB,无 swap |
| 磁盘 | 39G SSD(vda),系统盘 |
| 操作系统 | 银河麒麟高级服务器 V10(Tercel),内核 4.19.90-17.5.ky10.aarch64 |
| 数据库 | KingbaseES V009R001C010,企业版 180 天授权 |
| 安装与数据目录 | /opt/Kingbase/ES/V9 和 /data,端口 54321 |
| 压测工具 | kbbench,金仓自带,scale=50,约 500 万行 |

lscpu、free、系统版本与数据库版本的真实输出,确认 aarch64、麒麟 V10、KES V9 这套组合。
二、先钉死基线
调优最忌讳的一件事,改了一堆参数,然后凭感觉说「快了」。所以动手之前,先在完全默认的出厂参数下(shared_buffers=128MB、work_mem=4MB 这种状态)压一轮,把起点钉死。
工具用金仓自带的 kbbench。数据规模 scale=50,约 500 万行,4 并发 4 线程,select-only 负载跑 60 秒,模拟最常见的只读业务。
跑出来,5 次采样 55,481、49,076、49,648、54,029、62,723,取中位数 TPS 54,029。
这就是基线。后面所有对比都用同一份数据、同一条压测命令,唯一的变量是数据库参数。

默认参数下 kbbench 4 并发 60 秒连续执行 5 次,采样值 55,481、49,076、49,648、54,029、62,723,中位数 TPS 54,029。
三、KWR 报告,先搞清楚数据库在忙什么
按不少人的习惯,拿到基线就该开始调参了。我停了一下,先回答一个问题,瓶颈到底在哪。金仓给的工具是 KWR,全称 Kes Workload Repository,用过 Oracle AWR 的朋友会觉得非常眼熟,思路一模一样,打两个快照,生成一份报告,把这段时间数据库的负载、等待事件、Top SQL 全部摊开给你看。
开启也不复杂。建一个 sys_kwr 扩展,把 track_sql、track_instance 这些统计参数打开,压测前后各打一次快照,再用 perf.kwr_report_to_file 把报告导出来。

通过 perf.kwr_report_to_file 生成报告,快照列表完整覆盖了压测周期。
报告摊开,信息量很大,但真正关键的就几行。
DB Time 一共 328.17 秒,其中 DB CPU 占了 328.15 秒,99.99%。数据库的时间几乎全部花在计算上,这是一个纯得不能再纯的 CPU 密集负载。
再看缓存,Buffer Hit 100.00%,Buffer NoWait 也是 100.00%,500 万行数据基本全躺在共享缓冲区里,磁盘每秒的写操作只有 4.47 次,约等于没有。等待事件那一栏更直接,Top 等待清一色是 DB CPU,IO 类等待加起来总共 0.03 秒,占 0.01%,磁盘彻底洗清了嫌疑。


报告里的负载概览,DB Time 328.17 秒,DB CPU 328.15 秒占 99.99%,Buffer Hit 100.00%、Buffer NoWait 100.00%,磁盘每秒写仅 4.47 次。

Top 等待事件几乎全是 DB CPU,IO 类等待只占 0.01%。
还有一个数字我盯了很久。Parse Calls 每秒 51,060 次,而 Parse Reused 是 0.00%。每条 SQL 每次执行都在从头解析,一次复用都没有。这次没来得及处理它,但它明摆着是下一步的优化空间,文末再提。
Top SQL 也很干净,kbbench 的主查询 SELECT abalance 一条占掉 91.95% 的数据库时间,执行了 617 万次。

Top SQL 统计,SELECT abalance 占数据库时间 91.95%,执行 617 万次。
到这里,诊断结论其实已经写好了。这个场景下加内存没用,数据本来就全在内存里。换更快的盘更没用,IO 压根不是瓶颈。真正值得投入的只有一件事,让 CPU 少干活。这份十分钟出来的报告,省掉的是盲目堆硬件的冤枉钱,也直接定下了后面调优的方向,减少解析开销,优化执行路径,把内存用对地方。
四、参数怎么调
方向定了,接下来针对 4 核、7.3G 内存这个小配置做适配。改动全在下面这张表里,每条都写了我的理由。
| 参数 | 默认值 | 调优值 | 为什么这么调 |
|---|---|---|---|
| shared_buffers | 128MB | 1536MB | 约 20% 物理内存,扩大数据缓冲,同时给系统页面缓存留空间 |
| effective_cache_size | 4GB | 4608MB | 约 60% 内存,让优化器对可用缓存有正确预期 |
| work_mem | 4MB | 16MB | 匹配 4 并发下的排序和哈希需求 |
| maintenance_work_mem | 64MB | 256MB | 给 VACUUM 和建索引提速 |
| max_connections | 100 | 200 | 预留并发连接余量 |
| checkpoint_completion_target | 0.5 | 0.9 | 把刷盘摊平,压低 IO 峰值 |
| wal_buffers | 4MB | 16MB | 提升 WAL 写入吞吐 |
| max_wal_size | 1GB | 2GB | 降低 checkpoint 频率 |
| synchronous_commit | on | off | 测试场景看吞吐上限,生产环境按业务要求权衡 |
| track_sql / track_instance / track_io_timing 等 | off | on | 给 KWR 深度诊断喂数据 |
这里面有两个点想单独说说。
一个是 shared_buffers 不是越大越好。总共就 7.3G 内存,我取了 1536MB,大约 20%,给操作系统的页面缓存留足空间。数据库缓存和文件系统缓存抢内存,是小内存机器上很经典的翻车姿势。另一个是 effective_cache_size 必须跟着一起调,它本身不占内存,只是告诉优化器系统里大概有多少缓存可用。这个值要是留着默认不动,优化器会低估可用缓存,然后在执行计划的选择上变得保守甚至出错。
还有 synchronous_commit=off 这条要说清楚,这是测试场景为了看写入吞吐上限才关的。生产环境关不关,得按业务对数据丢失的容忍度来权衡,这一条不能照抄。
参数以追加方式写进 kingbase.conf,重启数据库,用 sys_settings 确认全部生效。

调优参数追加写入 kingbase.conf,每条都注明了适配 4 核 7.3G 配置的理由,随后重启生效。

重启后 sys_settings 的输出,shared_buffers 显示 196608 个 8kB 页,正好 1536MB,其余参数同样已生效。
五、SQL 层的两个对照实验
参数解决的是数据库层面的问题,SQL 优化解决的是语句层面的问题。很多人对 SQL 优化的全部理解就是一句话,「慢就加索引」。我在一张 200 万行的表上做了两组对照实验,结果正好一正一反。
先建表,id、val、created 三个字段,插 200 万行,created 按秒递增,具体语句见附录。
第一组,对 created 做范围查询,查 1 月 1 号到 15 号之间的数据,命中大约 40 万行,占全表 20%。没索引的时候走 Parallel Seq Scan,222ms。然后我把索引建上,再跑一遍。
优化器看了一眼索引,没理,还是 Parallel Seq Scan,179ms。

created 范围查询的 EXPLAIN ANALYZE 对比,命中约 20% 的行,无索引 222ms,建了索引优化器仍选 Seq Scan,179ms。
第一次碰到这种情况的人多半会懵,索引白建了?其实优化器是对的。命中 20% 的行还硬走索引,等于要做几十万次随机 IO 回表,成本比顺着把表扫一遍高得多,它不用你的索引,恰恰说明它算得清这笔账。这种查询真正的优化方向不是索引,是从源头减少扫描量,做分区裁剪,或者把过滤条件收得更紧。
第二组,对 id 做精确匹配,id = 1234567,200 万行里就找这一行。没索引,Parallel Seq Scan,94ms。建上索引,Index Scan,0.034ms。
差了大约 2700 倍。

id 精确匹配的对比,无索引全表扫描 94ms,有索引走 Index Scan,仅 0.034ms。
同样是加索引,一边几乎无感,一边是数量级的碾压,分水岭就在选择性上。返回行数占比越低,索引越值钱,占比一高,索引就成了摆设。
所以我现在的习惯是,碰到慢 SQL,第一件事永远是 EXPLAIN ANALYZE,看三样东西,一看走的是什么访问路径,Seq Scan 还是 Index Scan,二看命中行数占全表多大比例,三看有没有多余的排序、重复扫描、隐式类型转换。优化器不傻,它只能基于你喂给它的东西做决策,统计信息、索引、参数,这些就是它的全部依据。调优的人真正的价值,是把信息喂对。
六、复测对比
参数全部生效之后,用和基线一模一样的条件复测,同一份数据,4 并发,60 秒。
TPS 中位数从 54,029 涨到 70,541,提升 30.6%(均值提升 22.0%)。为了排除偶然,两组配置均多次采样后结论稳定,趋势一致。

调优后同条件复测,连续执行 5 次,采样值 70,541、70,614、59,106、62,362、70,558,中位数 TPS 70,541。
| 指标 | 调优前(默认参数) | 调优后 | 变化 |
|---|---|---|---|
| TPS 中位数(5次采样) | 54,029 | 70,541 | +30.6% |
| 采样波动范围 | 27.8% | 19.5% | — |
| Buffer Hit | / | 100% | 无 IO 瓶颈 |
| DB CPU 占比 | / | 99.99% | 纯 CPU 场景 |
| 高选择性 SQL(加索引前后) | 94 ms | 0.034 ms | 约 2700 倍 |

调优前后 TPS 对比,数据来自本机 kbbench 实测,2026-08-05,同一配置采样 5 次取中位数,统一关闭统计采集,基线为默认参数,调优参数见第四节。
30.6% 这个数字(按中位数),单看不算惊艳,我也不打算把它吹成什么奇迹。但要注意前提,默认配置在这个场景下已经把缓冲命中率打到了 100%,内存层面基本没有油水可榨,这 30.6% 是从 CPU 解析和执行路径的开销里挤出来的,在这种负载下已经算相当实在的收益了。而且这是纯只读场景,如果业务里有大量写入,checkpoint 平滑、WAL 缓冲加大和 synchronous_commit 这些调整的效果会比现在明显得多。
调优这件事,与其指望某个一改就起飞的魔法参数,不如老老实实把每一分硬件资源都用到位。
七、写在最后
这轮实验做完,我自己留下三条心得。
先诊断,再动手。KWR 报告十分钟就确认了「CPU 密集、IO 无瓶颈」这个关键事实,后面所有动作都是照着这个结论做的,一步冤枉路没走。反过来,不看报告直接调参,多半是在碰运气。
参数要贴着硬件和负载调。同一套参数换个机器规格未必还对,这也是我反复强调 4 核 7.3G 这个前提的原因。
SQL 优化的钥匙是选择性,不是索引本身。低选择性的查询,索引建了也白建,高选择性的查询,一个索引换来 2700 倍。看懂 EXPLAIN ANALYZE,比背十条优化口诀有用。
跑完这一轮我更确信,很多时候差的不是产品,是那套还停在出厂状态的默认参数。KWR 报告、EXPLAIN ANALYZE、一整套可调参数,工具都摆在那了,能不能压出性能,看的是会不会用。
全部实验都可以照着下面的清单复现,有兴趣的朋友可以在自己的环境里跑一遍。
附录 可复现命令清单
# 1. 环境确认
lscpu | grep -E 'Architecture|CPU\(s\):|Model name'
free -m; cat /etc/kylin-release
# 2. 启动数据库(如未启动)
export LD_LIBRARY_PATH=/opt/Kingbase/ES/V9/Server/lib
/opt/Kingbase/ES/V9/Server/bin/sys_ctl -D /data -l /data/server.log start
# 3. 安装 KWR 扩展
/opt/Kingbase/ES/V9/Server/bin/ksql -U system -d bench -p 54321 -c "create extension sys_kwr;"
# 4. 初始化压测数据(scale=50, 约500万行)
/opt/Kingbase/ES/V9/Server/bin/kbbench -U system -p 54321 -i -s 50 bench
# 5. 基线压测(默认参数)
/opt/Kingbase/ES/V9/Server/bin/kbbench -U system -p 54321 -c 4 -j 4 -T 60 -S bench
# 6. 手工打 KWR 快照
ksql -U system -d bench -p 54321 -c "select * from perf.create_snapshot();"
# 7. 生成 KWR 报告(指定快照区间)
ksql -U system -d bench -p 54321 -c "select perf.kwr_report_to_file(1, 2, 'text', '/home/kingbase/kwr_out/kwr.txt');"
# 8. SQL 优化对比实验(200万行)
ksql -U system -d bench -p 54321 -c "create table t_slow (id bigint, val varchar(64), created timestamp);"
ksql -U system -d bench -p 54321 -c "insert into t_slow select g, 'data_'||g, timestamp '2026-01-01' + (g*interval '1 second') from generate_series(1,2000000) g;"
# 8.1 低选择性范围查询, 无索引时观察 Seq Scan
ksql -U system -d bench -p 54321 -c "explain analyze select count(*) from t_slow where created > timestamp '2026-01-01' and created < timestamp '2026-01-15';"
# 建索引后再跑一遍, 观察优化器是否仍选 Seq Scan
ksql -U system -d bench -p 54321 -c "create index idx_t_slow_created on t_slow(created);"
ksql -U system -d bench -p 54321 -c "explain analyze select count(*) from t_slow where created > timestamp '2026-01-01' and created < timestamp '2026-01-15';"
# 8.2 高选择性精确查询, 无索引与有索引对比
ksql -U system -d bench -p 54321 -c "explain analyze select * from t_slow where id = 1234567;"
ksql -U system -d bench -p 54321 -c "create index idx_t_slow_id on t_slow(id);"
ksql -U system -d bench -p 54321 -c "explain analyze select * from t_slow where id = 1234567;"
# 9. 调优后复测, 命令与第 5 步完全一致
/opt/Kingbase/ES/V9/Server/bin/kbbench -U system -p 54321 -c 4 -j 4 -T 60 -S bench
鲲鹏昇腾开发者社区是面向全社会开放的“联接全球计算开发者,聚合华为+生态”的社区,内容涵盖鲲鹏、昇腾资源,帮助开发者快速获取所需的知识、经验、软件、工具、算力,支撑开发者易学、好用、成功,成为核心开发者。
更多推荐


所有评论(0)