做企业级业务开发久了,都会碰到同一个难题:数据量越积越多,原本跑得顺畅的SQL慢慢开始变慢,轻则接口响应延迟,重则整个系统卡顿,甚至影响核心业务流转。尤其是用KingbaseES这款国产企业级数据库(Oracle兼容版)的项目,很多时候不是数据库本身性能不行,而是SQL写法、索引设计、参数配置没做到位,白白浪费了硬件资源。
这份调优指南,全是基于官方调优手册提炼的实战干货,没有虚头巴脑的理论,全是落地能用的优化方法。不管是刚接触KingbaseES的开发,还是负责运维的DBA,都能照着一步步操作,把慢SQL的性能拉回来。
一、基础优化:索引用好,调优就成功了一半
做SQL调优,最先下手、见效最快的一定是索引。索引就像是书本的目录,有了目录找内容不用一页页翻,没目录就只能逐页查找。但索引也不是越多越好,多了反而会拖慢数据插入、更新的速度,毕竟每次写数据都要同步维护索引,一定要按需设计。

1. 常用索引类型,选对比建多更重要
KingbaseES自带了多种索引类型,每种都有对应的适用场景,乱建索引不仅没用,还会增加运维成本,日常用到的这几种吃透就够了:
- Btree 索引:这是数据库默认的索引类型,基于B+树实现,也是日常业务最常用的索引。等值查询、范围查询、排序、MIN/MAX聚合查询,它都能完美适配,绝大多数业务表的索引,优先选Btree准没错。
sql
-- 创建Btree索引
create table t1 (id int, info text);
insert into t1 values(generate_series(1,100000), md5(random()::text));
create index i_btree on t1 using btree(id);
-- 适配等值、范围查询,执行速度提升极其明显
explain analyze select * from t1 where id < 10;
explain analyze select min(id) from t1;
- Hash 索引:依靠哈希算法实现,等值查询的速度极快,理论上一次检索就能定位数据。但短板也很明显,只支持等值匹配,但凡涉及范围查询、排序,索引直接失效,只会走全表扫描,适合纯等值查询的场景。
sql
create table t2 (id int, info text);
insert into t2 values(generate_series(1,100000), md5(random()::text));
create index i_hash on t2 using hash(id);
-- 等值查询效率拉满
explain analyze select * from t2 where id = 10;
-- 范围查询直接失效,走全表扫描
explain analyze select * from t2 where id < 10;
- Bitmap 索引:用位图结构存储索引数据,特别适合字段值重复率极高的场景,比如性别、状态、类型这类字段。支持多条件AND/OR组合查询,数据压缩比高,OLAP分析类场景用它很合适。
sql
create table test(a int4, b int4);
create index idx_t on test using bitmap(a);
insert into test select round(random()*3), round(random()*100) from generate_series(1,1000000);
analyze test;
-- 多条件组合查询,性能提升显著
explain analyze select count(*) from test where a = 1 and b > 50;
- GIN 索引:通用倒排索引,专门针对数组、全文检索这类多值字段设计,依靠关键字匹配位置,能快速筛选出目标数据,模糊查询、全文搜索场景离不开它。
sql
create table t3(id int, info text);
insert into t3 values(generate_series(1,10000), md5(random()::text));
create index i_t3_gin on t3 using gin(to_tsvector('english',info));
analyze t3;
-- 全文检索专属,普通索引根本没法比
explain analyze select * from t3 where to_tsvector('english', info) @@ plainto_tsquery('hello');
- BRIN 索引:块范围索引,体积特别小,维护成本也低,只会存储连续数据块的取值范围。适合按时间顺序插入的流水表、日志表,数据连续存储的场景用它性价比极高。
sql
create table t5(id int, name text);
insert into t5 values(generate_series(1,100000), md5(random()::text));
create index i5_brin on t5 using brin(id);
-- 流式数据、连续数据范围查询首选
explain analyze select * from t5 where id < 10;
2. 索引高级用法,避开常见坑
光知道建索引还不够,很多时候索引失效,都是因为没掌握这些实用技巧,实战中经常用到:
- 表达式索引:平时写查询经常会在字段上加函数,这时候普通索引直接失效,这时候就需要给计算后的结果建索引,从根源解决索引失效问题。
sql
create table t1(name text);
-- 针对大小写不敏感查询,建表达式索引
create index idx_t1 on t1(upper(name));
explain select * from t1 where upper(name) = 'ADA';
-- 复杂拼接表达式,记得加括号包裹
create table t1(id int, first_name text, last_name text);
create index idx_t1_fullname on t1((first_name || ' ' || last_name));
explain select * from t1 where (first_name || ' ' || last_name) = 'Ada B';
- 联合索引:多字段查询的时候,一定要遵循最左前缀原则,把筛选性强、查询频率高的字段放在前面,少字段能命中索引,才不会白建索引。
sql
create table student(id int, name text, school text);
create index idx_student on student(id, name, school);
-- 命中索引:匹配最左前缀、前两个字段
select * from student where id = 1;
select * from student where id = 1 and name = '张三';
-- 无法命中:跳过最左字段,直接查后面的字段
select * from student where name = '张三';
- 局部索引:不用给全表数据都建索引,只给常用查询范围内的数据建索引,既能缩小索引体积,又能减少维护成本,针对性极强。
sql
create table t1(id int);
insert into t1 values(generate_series(1,1000000));
-- 只给常用查询的id区间建索引
create index idx_t1_partial on t1(id) where id < 500;
-- 命中索引
explain select * from t1 where id < 400;
-- 超出范围,不走索引
explain select * from t1 where id > 400;
- 模糊查询优化:普通like '%xxx%'很难走索引,针对不同模糊匹配方式,用对应的索引写法,就能解决慢查询问题。
sql
-- 前缀匹配,Btree索引直接生效
create table t1(id int, name text collate "C");
create index idx_t1 on t1(name);
explain select * from t1 where name like 'abc%';
-- 后缀匹配,用反转函数建索引
create index idx_t1_reverse on t1(reverse(name) collate "C");
explain select * from t1 where reverse(name) like 'cba%';
-- 中间模糊匹配,开启TRGM扩展建索引
create extension sys_trgm;
create table t2(id int, name varchar(20));
create index idx_t2_trgm on t2 using gin(name gin_trgm_ops);
explain select * from t2 where name like '%abc%';
3. 索引日常维护,别建完就不管
索引不是一劳永逸的,后期不维护,照样会变慢,这几点运维一定要记牢:
- 定期清理无用索引:通过系统视图查看索引调用次数,长时间没被使用、idx_scan为0的索引,直接删掉,留着只会占用资源、拖慢写操作。
vbnet
select relname as 表名, indexrelname as 索引名, idx_scan as 扫描次数 from sys_stat_user_indexes order by idx_scan;
- 及时重建索引:大批量删除、更新数据后,索引会产生大量碎片,查询效率直线下降,重建索引就能恢复性能,操作前建议先做VACUUM清理。
css
-- 单表索引重建 reindex table t1; -- 单个索引重建 reindex index idx_t1;
- 控制索引数量:写入频繁的业务表,少建索引,一般一张表索引不超过5个;数据量小于1000行的小表,不用建索引,全表扫描比走索引更快。
二、进阶优化:看懂执行计划,用HINT精准干预
KingbaseES自带查询优化器,会自动生成SQL执行计划,但如果表统计信息不准、数据分布不均匀,优化器很容易选错执行路径,导致SQL变慢。这时候就得学会看懂执行计划,实在不行再用HINT手动干预,精准解决问题。
1. 执行计划怎么看,抓重点就行
不用死记执行计划的所有细节,抓准四个核心点,就能快速定位慢SQL瓶颈:
- 基础查看:explain命令只会生成预估执行计划,不会真正执行SQL,适合初步排查。
csharp
explain select * from t1 join t2 on t1.id = t2.id where t1.id < 1000;
- 实战排查:explain analyze会真正执行SQL,返回真实执行耗时、扫描行数,排查问题最精准。
csharp
explain analyze select * from t1 join t2 on t1.id = t2.id where t1.id < 1000;
- 重点关注项:大表有没有走索引、是不是全表扫描;多表连接方式是否合适;预估行数和实际行数偏差大不大;排序、聚合有没有用到磁盘临时文件。
2. 执行计划常见问题,一招解决
- 统计信息不准:这是最常见的问题,优化器误判数据量,选错执行计划,只需要更新表统计信息就能解决。
sql
-- 执行analyze更新统计信息
analyze student;
-- 重新查看执行计划,行数预估恢复正常
explain select * from student where sno > 2;
- 缺少索引导致全表扫描:千万级大表全表扫描,耗时轻轻松松几秒,建完对应索引,耗时直接降到毫秒级。
sql
-- 建适配索引
create index idx_t1_name on t1(name text_pattern_ops);
analyze t1;
-- 索引扫描替代全表扫描,性能大幅提升
explain analyze select * from t1 where name like 'test99%';
- 内存不足排序变慢:大表排序、聚合时,work_mem内存不够,数据库会用到磁盘文件,速度极慢,临时调大内存参数即可解决。
sql
-- 会话级调大排序内存
set work_mem = '64MB';
explain analyze select * from big order by id;
3. HINT手动干预,慎用但要会用
只有优化器明显选错执行计划时,再用HINT干预,不要上来就强行指定。使用前需要在配置文件开启enable_hint = on,语法用/*+ 指令 */格式即可。
- 指定扫描方式:强制走索引或者全表扫描,适配特殊场景。
sql
-- 强制索引扫描
explain select/*+IndexScan(t1 idx_t1)*/ * from t1 where id > 10;
-- 强制全表扫描
explain select/*+SeqScan(t1)*/ * from t1 where id = 20;
- 指定连接方式:小表用嵌套循环,大表用哈希连接,数据有序用归并连接。
csharp
-- 强制哈希连接,适配大数据量
explain select/*+HashJoin(t1 t2)*/ * from t1 join t2 on t1.id = t2.id;
- 指定并行、连接顺序:针对复杂查询,手动优化执行逻辑。
sql
-- 指定并行worker数量
explain analyze select/*+Parallel(t2 2)*/ t2.id from t2,t3 where t2.id=t3.val group by t2.id;
-- 指定表连接顺序
explain select/*+leading(t3 t2 t1)*/ * from t1,t2,t3 where t1.id=t3.id and t3.val=t2.id;
4. HINT使用注意
能通过更新统计信息、调参数解决的,就别用HINT;HINT只对当前SQL生效,不会影响其他业务;表结构、数据大变后,记得重新检查HINT是否还适用,避免反而拖慢SQL。
三、高级优化:参数调优+并行查询,榨干硬件性能
索引和执行计划优化完,还有性能瓶颈,就该从数据库参数、并行查询下手,充分利用服务器CPU、内存资源,突破单线程性能限制。
1. 核心性能参数,按需调整
参数不是盲目调大,要结合服务器配置、业务类型修改,重点改这几类:
- 成本参数:优化器判断执行计划的依据,SSD硬盘可以适当调低随机访问成本,让优化器更倾向于走索引。
| 参数名 | 默认值 | 说明 | 调整建议 |
| seq_page_cost | 1.0 | 全表扫描单数据块成本 | SSD硬盘可调至0.5 |
| random_page_cost | 4.0 | 索引扫描单数据块成本 | SSD硬盘可调至2.0 |
- 内存参数:直接影响排序、连接、缓存性能,是优化重点。
- work_mem:排序、哈希操作专用内存,默认1MB太小,大查询容易落磁盘,会话级可调至16MB-64MB;
- shared_buffers:数据库共享缓冲区,建议设为物理内存的20%-80%,别超过系统可用内存一半;
- maintenance_work_mem:索引创建、数据清理的维护内存,大批量操作时可调大。
ini
-- 会话级临时调整
set work_mem = '16MB';
-- 全局调整,需重启数据库生效
alter system set work_mem = '16MB';
- 节点开关参数:临时禁用不合理执行节点,规避优化器缺陷,不到万不得已不轻易修改。
ini
-- 禁用哈希聚合
set enable_hashagg = off;
-- 谨慎使用:禁用全表扫描,无索引会直接报错
set enable_seqscan = off;
2. 并行查询,多核CPU提速
大数据量查询、全表扫描、聚合统计,单线程跑太慢,开启并行查询,把任务分给多个CPU核心同时处理,速度能成倍提升。
- 核心参数配置:先调整系统后台进程数,再配置并行worker数量,根据CPU核心数合理设置,别过度占用资源。
sql
-- 系统最大后台进程,需重启
alter system set max_worker_processes = 16;
-- 单查询最大并行worker数
alter system set max_parallel_workers_per_gather = 4;
- 使用注意:小表别开并行,启动进程的成本比执行还高;高并发场景少用并行,避免CPU、内存争抢;worker数量不是越多越好,超过4个性能提升就不明显了,反而会占用资源。
四、实战技巧:SQL改写+物化视图,懒人优化法
很多时候SQL慢,不是索引和参数的问题,而是写法太笨拙,稍微改改写法,或者用物化视图,就能轻松解决问题。
1. SQL语句改写,这几条必改
- UNION转UNION ALL:确认数据没有重复,一定要用UNION ALL,UNION会多一步去重排序,极其消耗性能。
sql
-- 低效
select * from t1 union select * from t2;
-- 高效
select * from t1 union all select * from t2;
- 清空表别用DELETE:DELETE是逐行删除,速度慢还产生垃圾数据,清空全表直接用TRUNCATE,秒级完成。
sql
truncate table t1;
- 杜绝隐式类型转换:索引字段类型和查询参数不一致,索引直接失效,一定要保证类型匹配。
- 相关子查询转连接查询:子查询逐行执行效率极低,改成JOIN连接,一次扫描就能出结果。
- 严禁无连接条件多表查询:极易产生笛卡尔积,数据量爆炸式增长,直接卡死查询。
2. 物化视图,复杂查询救星
报表统计、多表聚合这类复杂查询,每次执行都要耗时很久,而且基表数据更新不频繁,就用物化视图,提前把查询结果存起来,查询的时候直接读结果,不用重复计算。
sql
-- 创建物化视图
create materialized view mv_school_student as
select school, count(*) as student_count, avg(age) as avg_age
from student
group by school;
-- 基表数据更新后,刷新视图
refresh materialized view mv_school_student;
-- 直接查询,速度极快
select * from mv_school_student where school = '一中';
五、调优工具与监控,持续优化不反弹
SQL调优不是一次性工作,必须借助工具实时监控,及时发现新的慢SQL,才能保证系统长期稳定运行。
1. SQL监控工具
开启内置的SQL监控功能,实时跟踪SQL执行耗时、CPU消耗,快速定位TOP慢查询。先在配置文件加载对应插件,再创建扩展,就能查看监控数据、生成监控报告。
ini
shared_preload_libraries = 'plsql, sys_stat_statements, sys_sqltune'
sql_monitor.track = 'all'
sql
-- 创建监控扩展
create extension sys_sqltune;
-- 查询耗时最长的TOP10 SQL
select sql_text, duration, cpu_time from V$SQL_MONITOR order by duration desc limit 10;
2. 索引推荐与调优报告
不用自己瞎猜索引怎么建,通过系统插件,数据库能自动分析查询条件,给出索引建议;还能直接生成调优报告,包含索引优化、SQL改写方案,新手也能照着优化。
sql
-- 查看索引推荐
select * from index_recommendation_by_qual;
-- 生成SQL调优报告
select PERF.QUICK_TUNE_BY_SQL('select * from t1 where id = 1');
六、实战调优流程,照着做不出错
实战调优别盲目下手,按照这个步骤来,一步步定位解决,效率最高:
- 先抓TOP慢SQL:通过监控视图、工具,找出执行频繁、耗时最长的问题SQL;
- 收集基础信息:查看表结构、现有索引,更新表统计信息;
- 分析执行计划:用explain analyze定位瓶颈,是全表扫描、索引失效还是内存不足;
- 分层优化:先改索引、优化SQL写法,再调参数、用HINT,最后上并行、物化视图;
- 验证效果:对比优化前后执行耗时,确认性能提升;
- 长期监控:防止后续数据变化、业务改动,导致SQL再次变慢。
最后总结
KingbaseES SQL调优从来不是靠某一个绝招,而是层层递进的系统性工作。先把基础的索引、SQL写法优化到位,就能解决80%以上的慢SQL问题;剩下的复杂场景,再通过执行计划分析、参数调优、并行查询来解决。
日常业务里,OLTP高并发短查询,重点放在索引设计、SQL规范、参数微调;OLAP大数据量分析,侧重并行查询、物化视图、SQL改写。调优的时候切记循序渐进,改一项验证一项,别一次性批量修改,避免引发新的问题。
只要把这些实战方法用到位,KingbaseES的性能完全能满足企业级业务需求,再也不用被慢SQL困扰。