前言
一条 SQL 慢了,上网搜一圈,答案十有八九是调参数。shared_buffers 给多大,work_mem 设多少,并行开不开,评论区各说各的。照抄一遍,快没快不好说,库里倒是多出几个没人敢动的配置。
调参这件事,麻烦在于账没人算给你看。这篇把 KingbaseES 里常用的性能参数按用途归成几类,顺着真实排查的顺序讲,先看执行计划怎么把问题暴露出来,再对着问题找参数。
一、动手之前,先让执行计划开口
参数不该拍脑袋改。KingbaseES 的 EXPLAIN 能看执行计划,加 ANALYZE 会真跑一遍,把实际耗时、实际行数都摆出来。计划里 Sort Method、Disk、Memory 这些行,就是优化器留下的账本。
EXPLAIN ANALYZE
SELECT * FROM big ORDER BY id;
调参有条原则,解决方案的范围要跟问题的范围匹配。一条 SQL 的问题,就把它限制在语句级解决,动整个实例的参数,容易按下葫芦浮起瓢。
二、内存参数,见效最快的一组
先看 work_mem,排序和哈希操作能用的工作内存,默认只有 1024kB,也就是 1MB。排序超了就落磁盘,哈希连接、哈希聚集同理。复杂查询可能同时跑好几个排序或哈希,每个都按这个值要内存,几个会话再一起来,总量可能是它的好几倍。索引扫描不用它,可以放心。
拿一张灌了 400 万行的表做排序,这笔账看得最清楚。默认配置下计划提示排序走了 external merge,落盘 99648kB,执行时间 5611 毫秒。把 work_mem 调到 640000(单位 kB,约 625MB)再跑,排序换成了内存里的 quicksort,占用 110514kB,执行时间 945 毫秒。判断方法就一条,计划里看到 external merge Disk,就是内存不够了。
SET work_mem = 640000;
EXPLAIN ANALYZE
SELECT * FROM big ORDER BY id;
再看 maintenance_work_mem,VACUUM、CREATE INDEX、加外键这类维护操作用的内存,默认 16MB。灌数据建大索引的时候可以调大,索引数据上到 10g、内存又充裕,调到 1g 也不算过分,灌完记得调回来。一个会话同一时刻只有一个维护操作,这个值设得比 work_mem 大是安全的。
shared_buffers 是数据缓存,常规建议是不低于内存的两成,不超过八成。别忘了算总账,KingbaseES 的内存用量大致是 shared_buffers 加 wal_buffers,加 maintenance_work_mem,加并发排序连接数乘 work_mem,再加每个连接 1MB 的进程栈。这些加起来超了物理内存,换来的就是交换。
三、成本参数,优化器的价目表
优化器选计划靠比价。比价用的单价,就是下面这组参数。
| 参数 | 默认值 | 含义 |
|---|---|---|
| seq_page_cost | 1.0 | 顺序扫描读一个数据块的成本 |
| random_page_cost | 4.0 | 索引扫描读一个数据块的成本 |
| cpu_tuple_cost | 0.01 | 处理一个元组的 CPU 成本 |
| cpu_index_tuple_cost | 0.005 | 从索引处理一个元组的 CPU 成本 |
| cpu_operator_cost | 0.0025 | 执行一个操作符的 CPU 成本 |
这些都是相对值,seq_page_cost 是基准。random_page_cost 等于 4,意思是随机读一块的开销按顺序读的 4 倍计。数据库换了更快的磁盘,比如上了 SSD,可以把前两项调低,常见的做法是调到 0.5 和 2.0。CPU 明显升级了,CPU 那三项同理往下调。这套参数动的是优化器的判断,平时别碰,硬件换代了才值得动。
四、节点开关参数,救急的工具
enable 开头的一族参数,管的是执行计划的积木块。扫描类有 enable_seqscan、enable_indexscan、enable_bitmapscan,连接类有 enable_hashjoin、enable_nestloop、enable_mergejoin,聚合排序类有 enable_hashagg、enable_sort。
用之前要明白它的工作原理。关掉某个开关,优化器只是把这种路径的成本加到极大,压低它被选中的概率,没有硬禁止。表上没有索引时把 enable_seqscan 关了,计划照样全表扫。
这组开关的用武之地是救急。拿一张百万行的表按 val 分组计数,默认走 HashAggregate。
EXPLAIN SELECT count(*) FROM t2 WHERE id > 100 GROUP BY val;
-- HashAggregate (cost=21924.43..21974.45)
SET enable_hashagg = off;
EXPLAIN SELECT count(*) FROM t2 WHERE id > 100 GROUP BY val;
-- GroupAggregate (cost=116570.66..124119.82)
两种聚合各有脾气。分组字段的唯一值多,哈希聚合吃内存,性能掉得猛;GroupAggregate 内存消耗恒定,速度慢些但稳。注意关掉之后成本从 2 万涨到了 12 万,靠开关换来的计划本来就更贵,只有内存真扛不住时才划算。
用开关之前还有一步。执行计划不理想,常见原因是统计信息过期,先跑 ANALYZE,很多问题到这就结了。真要用开关,尽量放进语句级 HINT,别改全局。
五、并行参数,四个数字的套娃
并行查询的参数层层管制,关系是固定的。
max_parallel_workers_per_gather + max_parallel_maintenance_workers
<= max_parallel_workers
<= max_worker_processes
最外层 max_worker_processes 默认 8,改完要重启数据库。想并行的表还有门槛,min_parallel_table_scan_size 默认 8MB,表比这小不并行。parallel_setup_cost 默认 1000、parallel_tuple_cost 默认 0.1,这俩调小,优化器会更愿意并行。另外 dynamic_shared_memory_type 别设成 none,并行靠动态共享内存通信。
| 参数 | 默认值 | 作用 |
|---|---|---|
| max_worker_processes | 8 | 系统最大后台进程数,需重启生效 |
| max_parallel_workers | 按需设 | 并行 worker 总数,不超上一项 |
| max_parallel_workers_per_gather | 按需设 | 单条查询能拿到的并行数 |
| min_parallel_table_scan_size | 8MB | 触发并行扫描的表大小门槛 |
| parallel_setup_cost | 1000 | 启动并行的成本,调小更易并行 |
并行是优化器算出来的,不是开了就用。造一张 1000 万行的 student 表试试,查 age = 6 时走了并行,计划里给出两个 worker。
EXPLAIN SELECT * FROM student WHERE age = 6;
-- Gather (cost=1000.00..102331.58 rows=50000)
-- Workers Planned: 2
-- -> Parallel Seq Scan on student
EXPLAIN SELECT * FROM student WHERE age < 50;
-- Seq Scan (cost=0.00..169248.60 rows=5012113)
条件换成 age < 50,要返回一半数据,优化器算完账自己放弃了并行,改普通全表扫。表多大、索引多大,SELECT sys_size_pretty(sys_relation_size('student')); 一发就查出来。
还有个坑,HINT 里指定的并行数如果超过了 kingbase.conf 里 max_parallel_workers 的值,这条 HINT 不生效,只在系统日志里记一笔。排查并行没生效时,先看日志。
六、冷门但兜底的几个
表一多,优化器会换脑子。关联表超过 geqo_threshold(默认 12)个,KingbaseES 启用基因查询算法 GEQO,不再穷举所有路径。它的好处是规划时间可控,代价是选路带随机性,同样一条 SQL 可能给出不同计划。geqo_effort 默认 5,往 10 调,优化器肯多花时间,选中更优路径的概率也更大。
跟它绑在一起的还有 join_collapse_limit 和 from_collapse_limit,默认都是 8,管的是 JOIN 和子查询往 FROM 列表里重写的上限。这两个值设得太大,超过 geqo_threshold 就把 GEQO 招来了,可能弄巧成拙。
写游标的场景留意 cursor_tuple_fraction,默认 0.1,意思是优化器假设你只取一成数据,偏向快速返回首行。游标真要把数据全取完,把它往 1.0 调,计划会按总代价来算。
七、动参数的顺序和分寸
把顺序捋直了,调参就不玄。统计信息先刷新,一条 ANALYZE 解决一大片;单条 SQL 的问题用 HINT、改写、建索引,控制在语句级;会话级试验用 SET,验证有收益了再考虑进配置文件,落成实例级。
分寸上记三条。改之前把旧值记下来,给自己留退路;一次只动一个参数,快慢才有归因;灌数期和维护期的参数跟生产期分开,maintenance_work_mem 这类用完记得调回来。
参数是给优化器的输入,计划里的数字才是答案。先看账本,再动单价,这套流程走熟了,评论区那些五花八门的推荐值,你自己就能掂出轻重。