DB2的优化器是一个基于成本的优化器(CBO),它在为SQL生成执行计划时,依赖的是表、索引、列等数据库对象的统计信息。如果统计信息不准确,优化器就像拿着一张过期的地图找路,即使索引建得再合理,也可能选择错误的访问路径。runstats就是DB2提供的官方统计信息收集工具,用好它几乎是所有SQL调优工作的第一步。

统计信息到底包含什么,为什么必须收集
runstats收集的统计信息主要分为两大类:表级统计信息和列级统计信息。表级统计信息包括表的行数(CARD)、页数(NPAGES)、平均行长度等;列级信息包括列的唯一值数量(COLCARD)、最大最小值(HIGH2KEY、LOW2KEY)、列值的分布情况等。如果指定了索引相关的选项,还会收集索引的聚簇因子(CLUSTERFACTOR)、索引层数(NLEVELS)等关键指标。
这些信息存储在系统catalog表中,比如SYSCAT.TABLES、SYSCAT.COLUMNS和SYSCAT.INDEXES。优化器在解析SQL时会读取这些表,估算每一步操作的过滤基数,进而比较不同访问路径的成本。举个例子,一张订单表有一千万行数据,但统计信息还停留在三个月前一百万行的时候,优化器对过滤条件的估算就会严重失真,原本应该走索引的查询可能被判定为全表扫描更划算,结果性能急剧下降。
所以经验法则是:当表的数据量发生超过10%到20%的变化,或者大批量INSERT、UPDATE、DELETE、LOAD操作之后,都应该及时执行runstats。特别是LOAD操作,它是直接替换数据页的,load完成后统计信息会立刻过期,DB2甚至提供了LOAD命令的STATISTICS选项让收集和加载一步完成。
runstats的基础语法和常用写法
runstats必须在sysadm权限或者对该表有CONTROL权限下执行,基本语法是ON TABLE子句加统计范围选项。下面是几个最常用的写法。
-- 收集表和所有索引的统计信息(含分布信息) RUNSTATS ON TABLE db2inst1.orders WITH DISTRIBUTION AND DETAILED INDEX ALL; -- 只收集表本身的统计信息 RUNSTATS ON TABLE db2inst1.orders; -- 收集表和索引的基础统计信息(不含分布) RUNSTATS ON TABLE db2inst1.orders AND INDEX ALL; -- 收集指定列的分布信息 RUNSTATS ON TABLE db2inst1.orders WITH DISTRIBUTION ON COLUMNS (customer_id, order_status) AND DETAILED INDEX ALL;
这里有几个关键选项需要理解。WITH DISTRIBUTION会让DB2额外收集值的分布统计,通过分位数和频率统计信息来刻画数据倾斜的情况。比如status列90%的值都是COMPLETED,只有10%是PENDING,没有分布信息时优化器会假设均匀分布,估算就会出错。有了分布信息,WHERE status='PENDING'的查询才能被正确评估为低选择性而走索引。
DETAILED INDEX比普通的INDEX收集更细,它额外收集索引的聚簇因子,即数据行按索引键排列的有序程度。聚簇因子越接近1,走这个索引的范围扫描代价越低。代价是DETAILED模式收集更慢,占用的catalog空间也更大,一般只对关键查询涉及的核心索引使用。
执行完runstats之后,如果SQL的执行计划没有自动更新,可能是因为包的静态SQL还绑定在旧的统计信息上,这时需要执行REBIND命令重新绑定包。动态SQL则会在下一次执行时自动利用新统计信息重新编译。
大表的采样收集与实用技巧
对千万级以上的大表,全量runstats可能耗时很久,还会带来大量IO压力。这时候采样收集是更务实的选择,通过SAMPLING选项控制采样比例。
-- 使用25%的采样率收集分布统计 RUNSTATS ON TABLE db2inst1.big_table WITH DISTRIBUTION AND DETAILED INDEX ALL TABLE SAMPLE SYSTEM (25); -- 收集指定列分布,更细的采样控制 RUNSTATS ON TABLE db2inst1.big_table WITH DISTRIBUTION ON COLUMNS (city_name NUM_FREQVALUES 50 NUM_QUANTILES 100) AND INDEX ALL TABLE SAMPLE BERNOULLI (20);
SYSTEM采样按页抽样,速度快但精度略低;BERNOULLI采样按行随机抽样,精度更高但代价更大。采样率一般在10%到25%之间就能取得不错的估算效果,具体要看数据分布是否倾斜。对于分布严重倾斜的列,还可以通过NUM_FREQVALUES控制频率值的数量、NUM_QUANTILES控制分位数数量,让分布统计更精细。
除了手工执行,还有几个值得注意的实践点。第一,收集顺序很重要,如果使用ALTER TABLE ... ACTIVATE NOT LOGGED INITIALLY后大批量导入,应先收集表统计再收集索引统计,或者干脆用一条语句带上AND INDEX ALL一次性完成。第二,自动收集机制可以兜底,通过AUTO_MAINT和AUTO_TBL_MAINT数据库配置参数启用自动表维护,DB2会在后台评估统计信息的时效性并自动触发runstats,适合不想人工干预的场景。第三,可以用下面的SQL快速查看某张表上次的收集时间,判断统计信息是否新鲜。
-- 查看表的统计信息收集时间 SELECT TABNAME, STATS_TIME, CARD, NPAGES FROM SYSCAT.TABLES WHERE TABSCHEMA = 'DB2INST1' AND TABNAME = 'ORDERS'; -- 查看索引统计 SELECT INDNAME, STATS_TIME, NLEVELS, CLUSTERFACTOR FROM SYSCAT.INDEXES WHERE TABSCHEMA = 'DB2INST1' AND TABNAME = 'ORDERS';
如果STATS_TIME显示的时间明显早于最近一次大批量数据变更,或者CARD值与实际行数差距悬殊,就说明该重新跑一次runstats了。把统计信息的收集纳入日常运维计划,配合合理的采样策略,优化器才能持续做出正确的成本估算,这是SQL性能稳定的基石。
DB2 runstats统计信息SQL优化修改时间:2026-09-09 00:08:57