dbaai 发表于 2026-10-30 05:48:55

Sybase ASE 统计信息治理实战:从计划退化到 update statistics 与 optdiag 巡检

Sybase ASE 统计信息治理实战:从计划退化到 update statistics 与 optdiag 巡检

一、具体的问题

先说一个很多 ASE 老系统都会遇到的怪现象:某条查询过去几周都是毫秒级返回,业务没改代码、表也没怎么涨数据,某天突然变成十几秒;重启服务或者重建索引之后又恢复正常,过阵子又犯。还有一个更典型的:同一张表,查询条件里换个日期参数,计划就从索引 seek 掉到全表扫描,快慢差出两个数量级。

我自己在一个订单系统上就处理过:orders 表两千多万行,按 cust_id + order_date 查询,平时 200ms 以内,某个周一之后 P99 直接飙到 8 秒,应用层超时报错堆积。排查发现表数据其实只涨了 3%,罪魁祸首是统计信息——上一轮 update statistics 是七个月前跑的,这七个月里订单状态分布严重偏移(历史订单归档导致"已完成"占比从 20% 涨到 85%),优化器拿着陈旧的直方图估算行数,把一个明明可以走复合索引的查询估成了"返回 40% 的表",直接选了全表扫描。

这类问题的麻烦在于它不报错、不告警,只有业务方感受到慢。ASE 和有些数据库不一样,不会自动重建列的直方图,统计信息全靠 DBA 自己排程维护。很多从 MySQL、SQL Server 转过来管 ASE 的人,恰恰容易漏掉这一环。

二、核心原理

ASE 的基于成本的优化器做决策,依赖三类统计信息,全部存在系统目录里:


统计类型内容作用
列直方图(histogram)列值的分桶分布,每个桶记录上下界、频次、权重估算谓词选择性
密度(density)列(或列组合)的平均重复率估算等值匹配的返回行数
表/索引物理统计行数、页数、每页平均行数、页链信息估算 IO 成本


这些统计不是实时的。当你对表做大量 DML 之后,优化器手里的直方图和真实数据分布就脱节了——这就是"计划退化"的根源:查询没变,变的是优化器对数据分布的认知。

配套的治理命令有四个,职责要分清:


[*]update statistics 表 (列):给指定列建/更新直方图,最常用,只动列统计,不重建索引;
[*]update index statistics 表:更新索引全部键列(含复合索引的非前导列)的统计;
[*]update all statistics 表:列 + 索引键全量更新,开销最大,只适合维护窗口;
[*]delete statistics 表:删除列统计,用于清理无用的历史统计。


另外一个实用特性是采样:update statistics ... with sampling = N percent,大表不必全扫,采 10%~20% 通常足够优化器看清分布。更新统计信息后,ASE 会自动把引用该表的缓存计划作废、下次执行时重新编译,所以改完通常立刻见效,个别情况再配合 sp_recompile 补一刀。

三、实例参考(动手步骤)

步骤 0:确认现状基线

先别急着动手,把慢查询的执行计划和 IO 基线抓下来,否则治理完没法对比:


-- isql 会话内
1> set showplan on
2> go
1> set statistics io on
2> go
1> set statistics time on
2> go
1> select count(*) from pubdb..orders
   where cust_id = 12345 and order_date >= '2026-09-01'
2> go


记录 showplan 输出里的表访问方式(TABLE SCAN 还是索引名)、logical reads 和耗时,这就是你的"治理前"证据。

步骤 1:用 optdiag 还原统计信息现场

optdiag 是 ASE 自带的统计信息导出工具,把 orders 表的统计完整倒出来看:


optdiag statistics pubdb..orders -Usa -P'密码' -SSYB_ASE \
-o /tmp/orders_optdiag.txt


打开输出重点看两处:


[*]Last update of column statistics —— 列统计的上次更新时间。如果比业务高峰期早了好几个月,基本可以定性;
[*]直方图桶的分布 —— 对照实际数据分布,看关键列(如 order_status、cust_id)的桶是不是还反映现状。七个月前"已完成"订单只占 20%,直方图里最多的桶还停留在"处理中",这就是计划退化的直接证据。


步骤 2:更新统计信息

针对慢查询涉及的列精准补统计,先不动重型命令:


-- 1) 慢查询谓词涉及的列,各建直方图,步数从默认 20 提到 100
1> update statistics pubdb..orders (cust_id) using steps 100
2> go
1> update statistics pubdb..orders (order_date) using steps 100
2> go

-- 2) 复合索引的完整键列统计(含非前导列)
1> update index statistics pubdb..orders
2> go

-- 3) 大表可用采样降低开销(15.x 支持)
1> update statistics pubdb..orders (order_status)
   with sampling = 20 percent
2> go


命令执行完 ASE 会自动作废相关缓存计划;如果存储过程行为还怪,补一句 sp_recompile pubdb..orders 强制引用它的过程下次重编译。

步骤 3:验证计划恢复

重跑步骤 0 的同一查询,对比三项指标:


治理前:TABLE SCAN,logical reads 1,842,000,duration 8.2s
治理后:IX_orders_cust_date,logical reads 12,400,duration 0.19s


showplan 里访问方式从全表扫描回到索引扫描、logical reads 掉两个数量级,才算真的修复。没恢复就回去检查直方图步数是否够细、密度是否合理。

步骤 4:固化巡检,别再靠人肉

把 update statistics 排成每周维护作业,用 crontab 挂 isql 脚本:


# /opt/dba/scripts/update_stats.sh
#!/bin/sh
isql -Usa -P"密码" -SSYB_ASE -i /opt/dba/sql/update_stats.sql \
   -o /opt/dba/log/stats_$(date +%Y%m%d).log


update_stats.sql 内容按"大表采样、小表全量、热点表高频"分级:


-- 热点大表:采样 20%,每周
update statistics pubdb..orders (cust_id, order_date, order_status)
with sampling = 20 percent
go
-- 中小表:全量,每周
update statistics pubdb..customers
go


crontab 里每周日凌晨 2 点跑,业务低峰,单表串行执行避免抢 IO。

治理前后对比


指标治理前治理后
慢查询 P998.2s0.19s
logical reads184 万页1.24 万页
应用超时报错40+ 次/天0
统计信息最长滞后7 个月≤7 天
计划退化复发每月 2~3 次0(3 个月内)
治理投入—每周 15 分钟维护作业


四、实操检查清单


[*][ ] 给每台 ASE 实例建档:核心大表清单 + 各表上次 update statistics 时间;
[*][ ] 用 optdiag 每月导出一次核心表统计,归档留底,方便事后追溯分布变化;
[*][ ] 慢查询排查顺序固定:先 set showplan/statistics io 抓计划 → 再 optdiag 看统计新旧 → 最后才考虑加索引;
[*][ ] 直方图步数按列基数分级:低基数列默认即可,高基数列提到 100~200;
[*][ ] 大表统一用 with sampling = 10~20 percent,避免维护窗口超时;
[*][ ] update statistics 之后必跑一次核心 SQL 回归,确认 showplan 符合预期;
[*][ ] 存储过程异常时用 sp_recompile 强制重编译,不要盲目重启服务;
[*][ ] 每周维护作业的日志落盘并巡检,作业失败要有告警;
[*][ ] delete statistics 只用于清理确认无用的列统计,删前先 optdiag 备份;
[*][ ] 变更大表数据(批量导入、归档、大量 delete)之后,主动补一次相关列的 update statistics,不等周作业。


几个容易踩的坑


[*]一慢就重建索引。reorg rebuild 顺带更新部分物理统计,看起来"治好了",但列直方图还是旧的,过几周必复发。先 update statistics,再考虑 reorg。
[*]update all statistics 当日常手段。全量更新在千万级大表上能跑几十分钟还抢锁,放业务时间就是自己制造事故。日常用列级精准更新,全量只留维护窗口。
[*]只更新前导列。复合索引的非前导列也有独立查询场景,update statistics 表 只覆盖索引前导列,记得配 update index statistics 表。
[*]采样比例一刀切。分布极度倾斜的列(比如 95% 的行是同一个状态值)采样 10% 可能漏掉尾部分布,这类列要么全量、要么把采样提到 30% 以上。
[*]改完统计不验证。统计更新成功不代表计划一定变好,个别情况优化器仍选旧路径,必须用 showplan + statistics io 回归确认,形成闭环。


统计信息治理是 ASE 上性价比最高的优化手段:不花钱、不改代码、不动结构,一条命令换回两个数量级的性能。关键是把它从"救火动作"变成"每周例行",问题自然消失在发生之前。
页: [1]
查看完整版本: Sybase ASE 统计信息治理实战:从计划退化到 update statistics 与 optdiag 巡检