前言
在 KingbaseES 数据库里,SQL 执行计划好不好,直接决定了查询速度快不快!优化器能选出最优计划,全靠统计信息这个"关键依据"------要是统计信息缺了、过时了或者不准,优化器就跟"瞎猜"没啥区别,要么选错扫描方式,要么估错结果集大小,最后导致 SQL 执行效率拉胯。今天咱就从统计信息的基础逻辑唠起,拆解自动收集、主动收集、扩展统计这些核心调优手段,再配上实打实的实战代码,帮你把统计信息质量拿捏得死死的,让优化器每次都能选对执行计划!

文章目录
- 前言
-
- 一、统计信息到底是什么?优化器的"决策说明书"
-
- [1.1 统计信息的核心内容](#1.1 统计信息的核心内容)
- [1.2 为什么统计信息会影响性能?举个真实例子](#1.2 为什么统计信息会影响性能?举个真实例子)
- 二、自动收集:让数据库"主动"维护统计信息
-
- [2.1 核心配置参数(kingbase.conf)](#2.1 核心配置参数(kingbase.conf))
- [2.2 自动收集的适用场景与注意事项](#2.2 自动收集的适用场景与注意事项)
- [2.3 查看自动收集状态(实战代码)](#2.3 查看自动收集状态(实战代码))
- 三、主动收集:手动掌控统计信息更新时机
-
- [3.1 主动收集的常用场景](#3.1 主动收集的常用场景)
- [3.2 实战代码:主动收集统计信息](#3.2 实战代码:主动收集统计信息)
-
- [1. 全库收集(大库慎⽤,耗时久)](#1. 全库收集(大库慎⽤,耗时久))
- [2. 单表/指定列收集(推荐,影响范围小)](#2. 单表/指定列收集(推荐,影响范围小))
- [3. 带采样率的收集(大表提速关键)](#3. 带采样率的收集(大表提速关键))
- [4. 收集后验证统计信息准确性](#4. 收集后验证统计信息准确性)
- 四、扩展统计信息:解决多列相关性的"痛点"
-
- [4.1 扩展统计的三种类型](#4.1 扩展统计的三种类型)
- [4.2 实战代码:创建与使用扩展统计](#4.2 实战代码:创建与使用扩展统计)
-
- [1. 场景准备:存在函数依赖的表](#1. 场景准备:存在函数依赖的表)
- [2. 创建函数依赖扩展统计](#2. 创建函数依赖扩展统计)
- [3. 创建多元 N-Distinct 统计(优化 group by 场景)](#3. 创建多元 N-Distinct 统计(优化 group by 场景))
- [4. 创建多元 MCV 统计(优化多列条件查询)](#4. 创建多元 MCV 统计(优化多列条件查询))
- [5. 管理扩展统计对象](#5. 管理扩展统计对象)
- 五、统计信息调优避坑指南:这些错误别踩!
-
- [5.1 大表采样偏差导致统计不准](#5.1 大表采样偏差导致统计不准)
- [5.2 长期不更新统计信息](#5.2 长期不更新统计信息)
- [5.3 忽略多列查询的扩展统计](#5.3 忽略多列查询的扩展统计)
- [5.4 创建索引后未更新统计信息](#5.4 创建索引后未更新统计信息)
- 六、统计信息调优流程:从排查到优化的完整步骤
- 总结
一、统计信息到底是什么?优化器的"决策说明书"
简单说,统计信息就是数据库给表、列、索引做的"数据概况总结"------比如表有多少行、列里哪些值出现得频繁、值的分布情况咋样。优化器拿着这些信息,就能算出来查询要扫多少数据、耗多少 IO 和 CPU,进而挑出最省时间的执行计划。
1.1 统计信息的核心内容
- 表级信息 :表占了多少磁盘空间、总共有多少行数据,这些都存在
sys_class系统表里。 - 列级信息 :既有 NULL 值占比、列的平均宽度、唯一值数量这些基础信息,也有高频值、直方图、相关系数这些数据分布信息,全存在
sys_statistic系统表里。 - 扩展统计信息 :专门解决多列之间的关联问题------比如
a列和b列始终相等,单列统计看不出来,就得靠扩展统计来补位。
1.2 为什么统计信息会影响性能?举个真实例子
假设有张 10000 行的 student 表,sno 是 1~10000 递增的主键。要是统计信息没及时更,查 sno > 2 时:
- 优化器可能瞎估,以为只返回 3333 行,选了 Bitmap 索引扫描;
- 但实际要返回 9998 行(几乎是全表数据),这时候全表扫描比索引扫描快多了;
- 只要更一下统计信息,优化器立马选全表扫描,执行时间直接从秒级降到毫秒级,差距贼明显!
二、自动收集:让数据库"主动"维护统计信息
KingbaseES 默认就开着 autovacuum 这个后台进程,跟个"巡检员"似的,自动盯着表的数据变动。等变动到一定阈值,就会触发 ANALYZE 操作更统计信息,根本不用咱手动操心。
2.1 核心配置参数(kingbase.conf)
自动收集的规矩能靠参数改,按自己的业务场景调就行:
sql
-- 总开关:是否启用自动收集(默认开着)
autovacuum = on
-- 自动 ANALYZE 触发条件:表变动行数 >= 50 + 表总行数*10%
autovacuum_analyze_threshold = 50
autovacuum_analyze_scale_factor = 0.1
-- 最多能同时跑几个自动收集进程(默认 3 个)
autovacuum_max_workers = 3
-- 多久检查一次数据库变动(默认 1 分钟)
autovacuum_naptime = 1min
-- 执行时间超过 100ms 的自动收集操作记日志(方便排查问题)
log_autovacuum_min_duration = 100ms
2.2 自动收集的适用场景与注意事项
- 适合:日常业务里数据变动比较平缓的表,比如电商的用户表、订单表,不用咋管就能自动维护好。
- 要注意:
- 大表批量导入、更新后,自动收集反应会慢半拍,统计信息会暂时不准;
- 大表要是还按默认的 10% 阈值来,可能频繁触发收集,占系统资源,拖业务后腿。
2.3 查看自动收集状态(实战代码)
sql
-- 查 student 表的自动收集情况,一眼看清啥时候更过
select
relname as 表名,
last_vacuum as 最后一次vacuum时间,
last_analyze as 最后一次analyze时间,
n_dead_tup as 死亡元组数,
n_live_tup as 存活元组数
from sys_stat_user_tables
where relname = 'student';
-- 查当前正在跑的自动收集进程,看看有没有卡壳
select * from sys_stat_activity where usename = 'autovacuum';
-- 查自动收集的历史日志,确认是不是正常触发了
select * from sys_log where message like '%autovacuum%analyze%' order by log_time desc limit 10;
三、主动收集:手动掌控统计信息更新时机
自动收集虽然省心,但有些场景下必须手动触发 ANALYZE,才能保证统计信息实时准确,不然优化器还得瞎决策。
3.1 主动收集的常用场景
- 批量插入/更新/删除大量数据后(比如一次性导 100 万行,自动收集赶不上);
- 创建索引后(索引的统计信息得手动更,优化器才知道它有用);
- 执行计划预估不准时(比如实际返回 1000 行,优化器估成 10 行,明显不对)。
3.2 实战代码:主动收集统计信息
1. 全库收集(大库慎⽤,耗时久)
sql
-- 收集所有表的统计信息(大库千万别随便用,容易阻塞业务,尽量避高峰)
ANALYZE;
2. 单表/指定列收集(推荐,影响范围小)
sql
-- 收集 student 表所有列的统计信息,针对性强
ANALYZE student;
-- 只收集 student 表的 sno、sname 列,效率更高,不浪费资源
ANALYZE student(sno, sname);
-- 收集完查一下,确认是不是真生效了
select relname, last_analyze from sys_stat_user_tables where relname = 'student';
3. 带采样率的收集(大表提速关键)
大表全量收集太费时间,指定采样率就很香,能平衡效率和准确性(默认采样 30000 行):
sql
-- 采样 10% 的页面收集 student 表统计信息(大表优先选这个)
ANALYZE student TABLESAMPLE SYSTEM (10);
-- 数据分布不均匀时,用随机采样更精准,不会漏关键数据
ANALYZE student TABLESAMPLE BERNOULLI (10);
-- 小表或数据分布极不均匀时,直接全量收集,一步到位
ANALYZE student TABLESAMPLE SYSTEM (100);
4. 收集后验证统计信息准确性
sql
-- 查 table级统计信息,看看预估行数准不准
select
relname as 表名,
reltuples as 预估行数,
relpages as 页面数,
current_timestamp - last_analyze as 距上次更新时间
from sys_class
join sys_stat_user_tables on relname = relname
where relname = 'student';
-- 查 sno 列的详细统计信息,NULL 占比、唯一值这些都能看清
select
attname as 列名,
round(stanullfrac * 100, 2) as NULL值占比(%),
case when stadistinct < 0 then round(-stadistinct * 100, 2) || '%' else stadistinct::text end as 唯一值占比,
stawidth as 平均宽度(字节)
from sys_statistic s
join sys_attribute a on s.starelid = a.attrelid and s.staattnum = a.attnum
where s.starelid = 'student'::regclass and a.attname = 'sno';
四、扩展统计信息:解决多列相关性的"痛点"
单列统计信息根本看不出多列之间的关联------比如 a 列和 b 列始终满足 a = b,但优化器不知道,查 a=1 and b=1 时就会估错结果集大小。这时候就得靠扩展统计信息来补这个坑!
4.1 扩展统计的三种类型
- 函数依赖 :比如
b列的值全由a列决定(a = b),查多列条件时能精准估数,不瞎猜。 - 多元 N-Distinct 计数 :统计多列组合的唯一值数量(比如
group by a,b时的真实分组数),避免估错分组数。 - 多元 MCV 列表:统计多列组合的高频值,优化多列条件查询的选择率估算,让执行计划更优。
4.2 实战代码:创建与使用扩展统计
1. 场景准备:存在函数依赖的表
sql
-- 创建表 t,a 和 b 列满足 a = b(典型的函数依赖)
create table t(a int, b int);
insert into t select i%100, i%100 from generate_series(1, 10000) s(i);
analyze t;
-- 没创建扩展统计时,查 a=1 and b=1(优化器估数贼不准)
explain analyze select * from t where a=1 and b=1;
-- 输出会显示预估行数 1 行,实际 100 行,估数偏差大到离谱
2. 创建函数依赖扩展统计
sql
-- 创建函数依赖类型的扩展统计,让优化器知道 a 和 b 有关系
create statistics stts_dep(dependencies) on a, b from t;
-- 必须执行 analyze 才生效,别忘这一步!
analyze t;
-- 再查一次,优化器能正确估算返回 100 行,终于不瞎猜了
explain analyze select * from t where a=1 and b=1;
3. 创建多元 N-Distinct 统计(优化 group by 场景)
sql
-- 没创建扩展统计时,group by a,b 估数不准
explain analyze select count(*) from t group by a,b;
-- 优化器可能估成 1000 组,实际就 100 组,差了 10 倍
-- 创建多元 N-Distinct 扩展统计,统计多列组合的唯一值
create statistics stts_ndist(ndistinct) on a,b from t;
analyze t;
-- 再查,分组数估数精准,执行计划也更优了
explain analyze select count(*) from t group by a,b;
4. 创建多元 MCV 统计(优化多列条件查询)
sql
-- 创建多元 MCV 扩展统计,记录多列组合的高频值
create statistics stts_mcv(mcv) on a,b from t;
analyze t;
-- 多列条件查询,选择率估算更精准,执行速度直接提上来
explain analyze select * from t where a=5 and b=5;
-- 查扩展统计的详细数据,确认高频值是不是真被记录了
select * from sys_statistic_ext_data where statid = (select oid from sys_statistic_ext where statname = 'stts_mcv');
5. 管理扩展统计对象
sql
-- 查所有已创建的扩展统计,看看有没有没用的
select
statrelid::regclass as 表名,
statname as 统计名,
stattype as 统计类型,
created as 创建时间
from sys_statistic_ext;
-- 删掉不需要的扩展统计,省点资源
drop statistics if exists stts_dep;
五、统计信息调优避坑指南:这些错误别踩!
5.1 大表采样偏差导致统计不准
- 问题:大表采样比例太低,数据分布信息失真------比如直方图缺了关键区间,优化器直接估错。
- 解决:提高采样率或全量收集,必要时调整默认采样容量,别抠这点时间,准头更重要!
sql
-- 给大表提采样率(比如采样 20%),保证统计信息准
ANALYZE student TABLESAMPLE SYSTEM (20);
-- 调整默认采样容量(改完要重启数据库才生效)
alter system set default_statistics_target = 200;
select pg_reload_conf();
5.2 长期不更新统计信息
- 问题:有些历史表数据早变了,但统计信息还是几年前的,优化器拿着旧数据瞎决策,能不慢吗?
- 解决:设个定时任务,每天凌晨自动收集大表统计信息,一劳永逸!
sql
-- 定时任务示例(通过 crontab 每天凌晨 2 点跑,不影响业务)
0 2 * * * psql -U username -d dbname -c "ANALYZE student; ANALYZE order_info; ANALYZE product;" >> /var/log/kingbase_analyze.log 2>&1
5.3 忽略多列查询的扩展统计
- 问题:多列联合查询时,只靠单列统计,优化器看不出列之间的关联,选率估算偏差贼大。
- 解决:对经常用来连接、过滤的列组合,赶紧建扩展统计,别嫌麻烦!
sql
-- 给订单表的 user_id + order_date 列组合建扩展统计,兼顾函数依赖和高频值
create statistics order_ext_stats(dependencies, mcv) on user_id, order_date from order_info;
analyze order_info;
5.4 创建索引后未更新统计信息
- 问题:新建索引后,优化器压根不知道有这索引,还是傻乎乎走全表扫描,白建了!
- 解决:创建索引后立马执行
ANALYZE,让优化器知道有这"新武器"。
sql
create index idx_student_sno on student(sno);
ANALYZE student(sno); -- 只更这列的统计,效率更高,不浪费时间
-- 验证下索引是不是被优化器认出来了,别白忙活
explain analyze select * from student where sno = 100;
六、统计信息调优流程:从排查到优化的完整步骤
- 定位问题 :用
EXPLAIN ANALYZE对比预估行数和实际行数,差距超 50%,基本就是统计信息的锅; - 更新统计 :针对目标表执行
ANALYZE,大表记得指定采样率,别硬刚全量; - 验证效果 :再跑一遍
EXPLAIN ANALYZE,看看执行计划是不是变优了; - 进阶优化:要是多列查询还不准,就给相关列组合建扩展统计;
- 长期维护:调调自动收集参数,或者设个定时任务,保证统计信息一直准。
总结
统计信息就跟 KingbaseES 优化器的"眼睛"似的,只有信息精准,优化器才能看清数据、选对执行计划。日常调优时,别全指望自动收集------省心归省心,但关键场景(大表批量更新、多列查询)必须手动干预:用主动收集保证统计信息实时准,用扩展统计解决多列相关性的坑。把这些技巧用到位,就能从根上提升 SQL 执行性能,让数据库跑得又快又稳!
-
金仓官方网址:https://www.kingbase.com.cn
-
金仓社区链接:https://bbs.kingbase.com.cn/