接手那套金仓数据库的头一个月,我最大的感受是两眼一抹黑。
系统慢是显而易见的——页面转圈、报表超时、早高峰CPU飙到95%。但慢在哪里?是CPU不够用还是IO在等?是SQL烂还是参数没配好?没有人能告诉我。前几任运维留下的交接文档只有一句话:“系统性能一般,建议优化。”
怎么优化?从哪里开始?靠猜肯定不行。这时候就得靠工具说话。
金仓数据库自带了一套性能诊断工具链:SYS_KWR、SYS_KSH、SYS_KDDM,再加上sys_stat_statements、sys_qualstats、sys_sqltune这些插件。我刚接手的时候,这些工具一个都没装。花了几天时间全部部署好,跑了几天数据之后,系统的"真实面目"才算第一次浮出水面。
这篇文章就聊聊这套工具链在实战中怎么用、能解决什么问题、有哪些坑。不是工具文档的翻译版,是我自己用了两个多月的真实感受。
工具链全家福
先说清楚每个工具是干什么的,不然后面案例串起来会乱。
SYS_KWR(自动负载信息库) 是最核心的工具。它的工作方式类似Oracle的AWR——每隔一段时间自动给系统拍一张"快照",记录这段时间内的CPU使用率、等待事件、TOP SQL、IO统计、锁活动、内存使用等。然后可以生成一份报告,展示两个快照之间系统的"体检结果"。适合做宏观分析:系统在哪个时段最忙、瓶颈是什么资源、哪里需要调优。
SYS_KSH(活跃会话历史) 是KWR的补充。KWR记录的是分钟级别的聚合数据,但对于锁等待、瞬时CPU飙升这类秒级变化,KWR可能抓不到。KSH每秒采样一次所有活跃会话的状态——每个会话在做什么、在等什么、执行什么SQL。适合做微观排查:“早上9点03分那15秒系统卡住了,当时到底发生了什么。”
SYS_KDDM(性能诊断与建议) 基于KWR快照做自动分析。它会读两个快照之间的性能数据,然后输出优化建议——索引建议、GUC参数建议、SQL改写建议,每条建议都带预期收益。适合给经验不足的DBA当"参谋",也适合做调优前后的效果对比验证。
sys_stat_statements 跟踪所有SQL的执行统计信息,包括调用次数、总耗时、平均耗时、共享块读写、临时文件读写等。用来找TOP SQL最直接。
sys_qualstats 记录WHERE和JOIN条件中涉及的列。跑一段时间之后,可以发现有哪列被频繁过滤却没有索引。用来发现"遗漏的索引"很好用。
sys_sqltune 可以对单条SQL做深度分析,输出调优报告(包含索引建议和执行计划对比)。适合针对特定的慢SQL做精细化调优。
这套工具的定位是互补的:KWR告诉你"哪里有问题",KSH告诉你"问题发生时现场什么样",KDDM告诉你"可以怎么修",sys_stat_statements和sys_qualstats帮你定位具体是哪条SQL、哪张表。
部署配置:该开的参数一个都不能少
工具装好了不代表就能用了。有几个GUC参数如果不打开,KWR报告里的关键信息就是空的。
我最开始吃了个亏:装好KWR跑了三天,生成报告一看,"等待事件"那一栏全是空的——因为
track_wait_timing 默认是off。没开这个参数,数据库就不统计等待事件的耗时,报告里的Top 10前台等待事件全是零。
类似的关键参数还有几个,列出来免得有人踩同样的坑:
shared_preload_libraries = 'sys_kwr, sys_stat_statements' sys_stat_statements.track = 'top' track_sql = on # 记录SQL执行统计 track_instance = on # 记录实例级别统计 track_wait_timing = on # 必须开,否则等待事件无数据 track_counts = on # 默认on,统计信息收集开关 track_io_timing = on # 记录IO耗时,报告才有DataFileRead等数据 sys_kwr.track_os = on # 采集主机OS级别的CPU/IO/内存数据 sys_kwr.enable = on # 启用自动快照 sys_kwr.interval = 30 # 快照间隔(分钟),我设为30分钟 sys_kwr.history_days = 14 # 保留14天历史 sys_kwr.topn = 20 # TOP N 报表显示20条 sys_kwr.language = 'chinese' # 报告中文输出 sys_kwr.collect_ksh = on # 开启KSH秒级采样
改完之后要重启数据库。等快照积累了一两天之后,就可以正式用KWR来分析系统了。
实战一:KWR报告带我找到系统"心腹大患"
系统刚上线KWR监控的第一周,我每天做的事情就是导出前一天的KWR报告,逐项看。
生成报告的命令很简单:
SELECT * FROM perf.kwr_report( (SELECT min(snap_id) FROM perf.kwr_snapshots WHERE time >= NOW() - INTERVAL '1 day'), (SELECT max(snap_id) FROM perf.kwr_snapshots), 'text');
一份标准的KWR报告分三大部分:报告头、报告摘要、报告主体。我每次先看摘要,大多数问题在摘要里就能看出来。
第一周的某份报告摘要里,有一组数据让我盯了很久:
负载性能: DB Time(s): 2180.3 DB CPU(s): 892.5 前台等待时间(s): 1287.8 Top 10 前台等待事件: DataFileRead 43.2% 平均等待 8.3ms wal_insert 12.1% 平均等待 0.4ms transactionid 7.7% 平均等待 32.5ms DataFileWrite 5.4% 平均等待 5.1ms WALWriteLock 4.8% 平均等待 0.6ms
DB Time 2180秒,wall-clock时间60分钟(一个快照周期),说明平均有36个CPU核心同时处于忙碌状态。这台机器128核,理论上远没到极限,但DataFileRead占DB Time的43%——接近一半的时间花在等磁盘读数据上。
再看实例效率百分比:
实例效率百分比(理想值100%): Buffer Hit%: 85.2% In-memory Sort%: 92.3% Library Hit%: 97.1%
Buffer Hit%只有85%,意味着100次数据访问里有15次要读磁盘。这个命中率对于256GB内存的机器来说太低了。
两个数据放在一起,问题就很清楚了: shared_buffers配得太小,大量数据访问落到磁盘IO上,CPU在等IO完成。
打开系统配置一看,shared_buffers只有2GB。256GB的机器,给了数据库2GB的共享缓存。难怪Buffer Hit%只有85%。
把shared_buffers调到64GB之后,过了两天又出了一份KWR对比报告。这次Buffer Hit%升到了97%,DataFileRead的等待占比从43%降到了11%。DB Time也从2180秒降到了820秒。
这就是KWR最直接的价值: 你不需要猜系统哪里慢,报告会告诉你时间花在哪了。
后来我把每一周的KWR报告都导出来归档。月底做了一份趋势对比,发现随着数据量增长,Buffer Hit%在以每周约0.3%的速度缓慢下降。这让我提前了一个月预判到需要再次调整shared_buffers或增加硬件内存,而不是等用户抱怨了才去查。
KWR报告怎么看才算会看
刚开始接触KWR报告的人,容易被里面密密麻麻的表格吓到。我摸索了大概两周才形成自己的阅读顺序,现在每次打开报告就看四个部分,基本不会漏掉关键问题。
第一看负载性能表。 看DB Time和DB CPU的比值。如果DB Time远大于DB CPU,说明大量时间花在等待上(IO、锁等),而不是在计算。比值越大,等待越严重。一般超过2倍就需要关注了。
第二看Top 10前台等待事件。 超过5%的等待事件都要分析一下原因。DataFileRead高→优先查缓存命中率和索引命中率。transactionid高→优先查锁冲突和长事务。wal_insert高→优先查WAL参数和日志写入负载。
第三看实例效率百分比。 Buffer Hit%低于90%说明共享缓存不够或索引设计有问题。In-memory Sort%低于95%说明work_mem太小,大量排序走了临时文件。
第四看TOP SQL。 按DB Time排序,看排前几位的SQL是什么。很多时候看完前三条SQL就能找到系统的主要瓶颈。
这个顺序是从多个项目里总结出来的。先看宏观(负载和等待),再看微观(TOP SQL),而不是一上来就扎进细节里。
实战二:KSH秒级采样抓到"幽灵锁"
KWR擅长全景分析,但有一个盲区——对于持续时间短、发生频率不固定的瞬态问题,它可能抓不到。
举个例子:系统每天早上9点左右会出现几十秒的卡顿,然后就恢复了。KWR报告里没有明显的异常——因为快照是30分钟一个间隔,那几十秒的问题被平均到了30分钟的数据里,根本看不出来。
这就是KSH上场的时候了。
KSH每秒采样一次活跃会话的状态,记录每个会话在那一秒的pid、user、wait_event、执行的query、state等信息,存入内存的环形缓冲区。当问题发生之后,可以从历史表里回溯那一秒现场发生了什么。
配置KSH只是在已有的KWR配置里加了一行:
sys_kwr.collect_ksh = on
不需要额外装插件,重启后KSH就开始工作了。
遇到那个"早上9点卡顿"的问题时,我先在KSH的历史表里查了问题时段的数据:
SELECT ts, pid, wait_event, wait_event_type, state, queryFROM perf.ksh_historyWHERE ts BETWEEN '2024-03-15 09:00:00' AND '2024-03-15 09:01:00'ORDER BY ts, pid;
结果发现了三个会话在9点00分17秒到9点00分43秒之间,wait_event_type全部是"Lock",wait_event全部是"transactionid"。三个会话都在等同一个事务锁,而持有锁的第四个会话state是"idle in transaction",已经空转了将近25分钟。
KSH的秒级采样把整个过程还原了:
- 09:00:10 会话A(pid=12345)执行UPDATE,锁住记录后进入idle in transaction,未提交
- 09:00:17 会话B(pid=12346)执行同一行的UPDATE,开始等锁
- 09:00:19 会话C(pid=12347)也开始等锁
- 09:00:22 会话D(pid=12348)也开始等锁
- 09:00:43 被阻塞的会话超时,应用重试,循环同上
- …到09:08分左右,会话A因为应用层超时才被回收,锁释放
这个"25分钟的idle in transaction"在KWR的30分钟快照里只表现为"transactionid等待占比7.7%"这个抽象数字,根本看不出是哪个会话、哪条SQL导致的。KSH的秒级采样直接把现场还原了。
找到阻塞的源头之后,杀掉会话A的进程,卡顿立即消失。然后联系开发修复了事务代码中漏掉的commit分支。
从那以后,每次排查间歇性问题,我都会先查KSH的历史数据——三分钟的KSH数据比三十分钟的KWR数据能提供的信息量多得多。
实战三:KDDM的自动建议靠谱吗
KDDM是我最后才用起来的工具,因为它依赖KWR快照——没有快照积累,KDDM就分析不了。
KDDM的用法很简单:
-- 先生成两个快照(调优前和调优后对比)SELECT * FROM perf.create_snapshot();-- ...做一些操作或等一段时间...SELECT * FROM perf.create_snapshot();-- 生成KDDM报告SELECT * FROM perf.kddm_report(1, 2);
报告输出文本格式,包含几个部分:数据库时间分解、CPU相关建议、等待事件建议、完整SQL列表。每条建议都标注了优先级。
我第一次用KDDM是在调完shared_buffers之后,想看看它还能不能发现其他问题。报告里排在第一位的建议是:
建议编号: 3 优先级: 高 问题: 检查点过于频繁导致IO尖峰 建议依据: 检查点间隔平均4.8分钟,低于建议值15分钟 建议动作: 将checkpoint_timeout从5min增加到15min, max_wal_size从1GB增加到8GB, checkpoint_completion_target设为0.9 预期收益: 减少检查点IO尖峰约60%
我按建议改了参数,跑了一周之后再看,检查点间隔从4.8分钟变成了13.5分钟,IO曲线确实平滑了很多。这个建议的准确度和可操作性都不错。
但KDDM也不是每条建议都靠谱。有一次它的报告建议我把
work_mem 从32MB调到128MB,预期收益是"减少临时文件写入约40%"。我没有直接采纳——因为我算过并发数,600个并发连接如果都做排序,128MB ×
600 = 76.8GB,再加上shared_buffers的64GB,内存就快满了。KDDM不会替你考虑并发场景下的"放大效应"。
还有一次KDDM建议我创建三个索引,其中两个建上去之后效果确实明显,第三个建上去之后查询没快多少,反而因为维护这个索引拖慢了INSERT的性能。后来又把它删了。
所以我对KDDM的定位是: 一个经验丰富的"参谋",但不是决策者。它能在你忽略的地方提出建议,但不是每条建议都适合你的场景。采纳之前要想清楚业务场景和数据特征。
KDDM还有一个很有用的功能——GUC顾问:
SELECT * FROM perf.kddm_guc_advisor(600, 'OLTP', 128, 256);
根据并发数(600)、业务类型(OLTP)、CPU核数(128)、内存大小(256GB),输出推荐参数值。它给我的建议和我在实际调优中采用的参数基本一致——shared_buffers=64GB、work_mem=55MB、wal_buffers=16MB、max_wal_size=4GB。对于刚接手一套新系统、没有调优经验的人来说,这个GUC顾问可以作为一个不错的起点。
实战四:sys_qualstats发现的"隐形索引需求"
sys_qualstats这个插件很小,但它解决了一个很实际的问题: 你怎么知道哪列该建索引?
常规做法是看慢查询,分析执行计划,发现全表扫描然后建索引。但这个方法是被动的——你得先有用户抱怨慢,或者TOP SQL已经出问题了,才会去查。
sys_qualstats的思路不一样:它会记录所有SQL语句中WHERE和JOIN条件里用到的列,然后统计每列被引用的频率。跑一段时间之后,你去查它的输出:
SELECT * FROM sys_qualstats ORDER BY count(*) DESC LIMIT 20;
返回的结果把被引用的列按频率降序排列。排在前面的,就是"最常被查询条件使用的列"。
我第一次跑这个查询的时候,排在第一位的列让我吃了一惊——一张大表的
create_date 列,被27条不同的SQL在WHERE条件里引用,累计查询了几十万次,但
这张表上根本没有 create_date 的索引。
也就是说,有27条SQL每天都在这张表上做全表扫描,只是因为有一个过滤条件涉及 create_date。但因为每一条SQL本身可能只跑一两秒,从来没有进入过TOP SQL的视野,所以一直没有人发现这个问题。
在这个列上建了索引之后,相关SQL的总响应时间下降了约40%。而且这个改动是"零风险"的——如果建了索引没有效果,删除就是了,不会影响业务逻辑。
从那以后,我养成了一个习惯:每月跑一次sys_qualstats,看看有没有高频率被引用但没有索引的列。大部分月份都没有发现,但偶尔发现一条,就是一次"低挂果实"式的优化。
工具配合使用的几种场景
工具单独用各有各的用途,但真正发挥价值的时候,往往是几个工具配合起来用。
场景一:系统整体变慢
先用KWR看宏观。如果DataFileRead占比高,查Buffer Hit%确认是否是shared_buffers问题。如果是,调大shared_buffers后用KWR持续观察效果。如果不是,用sys_stat_statements找TOP SQL,分析执行计划。
场景二:某个时段固定卡顿
用KWR的"按小时"维度看哪个时段的DB Time异常高。定位到时段后,用KSH秒级采样数据查那个时段每个会话在做什么。找到阻塞源头(长事务、锁冲突),对照sys_stat_statements找到对应SQL。
场景三:调优后效果验证
调参数之前建一个KWR快照,调完参数一段时间后再建一个快照。用KDDM报告做前后对比,看等待事件占比的变化是否达到预期。或者直接用KWR的快照对比功能看DB Time、Buffer Hit%等核心指标的变化。
场景四:发现遗漏的优化点
用sys_qualstats找高频引用但无索引的列,用sys_stat_statements找平均执行时间异常增大的SQL(可能是统计信息过期导致执行计划变化),用KDDM的GUC顾问检查参数是否合理。
踩坑记录
工具用顺手了之后,我开始依赖它们,但依赖过头也会出问题。记几条教训。
第一个坑:KWR快照太多会占空间。 一开始我把
sys_kwr.interval 设成了5分钟,觉得数据越细越好。结果一周之后
perf.kwr_snapshots 表快照了2000多条,占了几十GB空间。而且报告生成速度变慢——因为要比较两个快照之间的大量数据。后来改成30分钟,14天保留,空间占用控制在5GB以内。
第二个坑:KSH的历史数据是内存环形缓冲区。 有一次一个间歇性问题发生了35分钟,我去查KSH历史,结果只能查到前10分钟的数据——因为环形缓冲区默认大小是10分钟采样量。后来把
sys_kwr.ringbuf_size 调大了才够用。建议根据实际情况估算需要的保留时长。
第三个坑:KDDM建议必须结合业务验证。 前面说了,KDDM建议加索引,但没考虑到那张表的INSERT频率很高,加了索引会拖慢写入。还有一次它建议把某个SQL改写成另一种写法,业务验证后发现两个结果集不完全一致——改写虽然性能好了但语义变了。所以KDDM的建议只能当"线索",不能当"指令"。
第四个坑:sys_stat_statements的数据是累计的,不是增量的。 刚配置的时候查sys_stat_statements,发现一些SQL的total_exec_time大到惊人。后来才意识到这是累计值——从数据库启动到现在一直在累加。如果要看某个时段内的变化,需要定时采样并计算差值,或者用KWR的TOP SQL报告(基于快照期间的增量数据)。
第五个坑:KSH只记录主库。 测试环境有主备架构,有一次想在备库上查KSH历史排查问题,发现视图是空的。查文档才知道KSH只在主库运行。备库上的问题排查还得靠其他手段。
工具链之外的一些经验
用工具用久了,对"工具能干什么、不能干什么"会有更清晰的认识。
KWR/KSH/KDDM这套工具链,强在"数据采集和可视化"——以前你需要自己写脚本采集的数据,它们自动帮你做了,还以报告的形式呈现。但它们不会替你"理解"数据。报告告诉你DataFileRead占43%,但它不会告诉你这43%是因为shared_buffers不够、还是因为索引缺失导致的全表扫描太多、还是因为IO调度器的配置不对。这三种原因的解决方案完全不同,需要你结合执行计划、系统配置、业务特征综合判断。
工具缩短了"发现问题"的时间,但没有缩短"分析原因"的时间。在我接触的案例里,"发现问题"只占整个调优过程的20%时间,剩下80%花在"这个现象是什么原因导致的"上面。而后者更多依赖对数据库原理的理解,而不是工具的使用熟练度。
还有一个容易被忽视的点:工具产生的是历史数据,不是实时数据。KWR报告反映的是"过去30分钟系统发生了什么",KSH反映的是"过去几秒到几分钟的采样"。当你拿到报告的时候,问题可能已经结束了。对于需要实时告警的场景,还得靠其他监控工具(如Zabbix、Prometheus等)来做实时阈值告警。KWR/KSH更适合"事后分析和预防性体检"。
建立工具使用的"工作流"
用了几个月之后,我逐渐形成了一套固定的工具使用流程,每周走一遍,大概花20分钟:
每天(自动): KWR自动快照,不需要人工干预。如遇性能问题,查KSH秒级数据定位现场。
每周(人工,20分钟): 导出一份KWR周报,扫一眼Top等待事件、Buffer Hit%、TOP SQL的变化趋势。如果某个指标连续两周恶化,列入关注列表。
每月(人工,40分钟): 跑一次sys_qualstats,检查高频引用的列是否有未建索引。检查sys_stat_statements里平均执行时间增长最快的SQL。跑一次KDDM GUC顾问,看看需不需要根据最新的负载调整参数。
每季度(人工,1小时): 做一次全面的KWR快照对比(季度初 vs 季度末),评估系统容量变化趋势,预测是否需要硬件扩容。
这套流程帮我在系统性能恶化之前就发现问题。最成功的一个案例是:KWR的Buffer Hit%连续三周每周下降0.3%,按这个趋势推算,两个月后会跌破90%。提前申请了内存扩容,在业务高峰期到来之前完成了升级,用户没有任何感知。
总结
回到最开始的问题:金仓数据库的性能诊断工具到底实不实用?
我的回答是:非常实用,但它们不是"智能"的——KWR不会替你想,KSH不会替你看,KDDM的建议也需要你判断。它们的作用是把以前需要手动采集的数据自动采集好、整理好、呈现出来,节省的是"信息获取"的时间,而不是"决策"的时间。
如果让我推荐一个"最先部署的工具",我会选KWR。不是因为它最强,而是因为它是其他工具的基础——KSH依赖KWR的配置框架,KDDM依赖KWR的快照数据。先把KWR装好跑起来,把基线数据建立起来,后面再按需加其他工具。
系统调优这件事,最怕的不是"问题有多复杂",而是"连问题在哪都不知道"。KWR/KSH/KDDM这套工具链,就是用来回答"问题在哪"的。剩下的,还是得靠基本功。