资讯动态

【金仓数据库征文】别急着加内存:一次 Oracle 迁国产库的实战复盘,慢 SQL 背后全是坑

发布时间:2026/8/13 23:33:08 来源:尧图企业网站定制
那天早上九点电话就开始响。省级平台刚切到新库首页那张综合报表转了十几秒点查询直接卡死前端弹了一堆“请求失败”。业务部门没明说但那意思我听得懂Oracle 上跑得好好的换完国产库怎么成这样了。其实脚本、存储过程、视图、触发器我们跟了大半年基本都找到了对应写法单元测试也跑过心里原本是有底的。真到上线高峰才发现“跑得动”和“跑得快”完全是两码事。那几天我没敢乱动参数。组里有人一上来就想加内存、拉满 shared_buffers我拦住了。太多“数据库不行”的锅最后查下来都是 SQL 没写好、统计信息没更新或者连接池太小。我们给自己定了个顺序先连库看现场再抓热点再看计划最后才动手。这话好说真到业务催命的时候能不能按住手才是关键。我把这套流程随手画在纸上贴在显示器边上每次想跳步就抬头看一眼连库这步看着不起眼。图形客户端点过去延迟 3ms四百多张表一口气刷出来起码说明网络、权限、字符集这些地基没问题。真要连库都磕磕绊绊后面查出来的“慢”很可能是假象。进了 ksql第一句我就按总耗时排序把最吃资源的 SQL 揪出来SELECTqueryid,calls,round(total_exec_time::numeric/1000,2)ASsec,round(mean_exec_time::numeric/1000,3)ASavg_secFROMsys_stat_statementsORDERBYtotal_exec_timeDESCLIMIT5;结果很直观一条统计报表 SQL 独占 126 秒平均 8.9 秒。别的语句在它面前基本可以忽略我又按物理读排了一遍怕漏掉那种单次不慢、但 I/O 积少成多的语句。sys_stat_statements 这个扩展我们上线前就配好了不然这些数据根本抓不到。顺便提一句要是你查出来是空先确认扩展建了track_activity_query_size 也够大不然 SQL 文本会被截断查了也白查。光看耗时不够得看它“怎么跑的”。给那条 SQL 加 EXPLAIN ANALYZE问题直接摆在桌面上t_apply_info 是 Seq Scan近百万行硬扫Filter 掉 88 万行。Rows Removed by Filter 是 882310这个数字我记得特别清楚——数据库费老大劲读了一百万行最后只留八万多。这种场景不建索引光靠堆内存是没用的。动手的时候我先建了个部分索引CONCURRENTLY避免锁表。业务只查 status1 的待办status 为别的行没必要进索引索引体积能小一大截。建完顺手 ANALYZE确认统计信息真的更新了。CREATEINDEXCONCURRENTLY idx_apply_ctimeONt_apply_info(create_time)WHEREstatus1;ANALYZEVERBOSE t_apply_info;但建完索引也不是万事大吉。金仓的优化器和 Oracle 脾气不太一样对成本估算偏保守。有条复杂报表 SQL怎么都不肯走新索引还是 Seq Scan。我没硬改业务逻辑而是用 Hint 强行引导了一下/* IndexScan(a idx_apply_ctime) */SELECTa.apply_no,a.create_time,u.user_nameFROMt_apply_info aJOINt_user uONa.user_idu.idWHEREa.create_time2026-08-01ANDa.status1ORDERBYa.create_timeDESCLIMIT50;Hint 这东西我原则是能不用就不用。它相当于告诉优化器“别猜了听我的”。万一数据分布变了Hint 反而帮倒忙。这次算是临时止痛药后面统计信息稳了又回来验证了好几遍。建完索引我顺手查了下表膨胀情况。PostgreSQL 系里死元组一多再大的索引也救不回来SELECTrelname,n_live_tup,n_dead_tup,last_vacuum,last_autovacuum,last_analyzeFROMsys_stat_user_tablesWHERErelnamet_apply_info;n_dead_tup 比例不高last_analyze 是刚更新的时间戳心里才踏实一点。SQL 层面理顺后高峰期还是偶尔抖。那台机器 64GB 内存数据库独占。组里有人主张 shared_buffers 直接拉满 32GB我以前在别的 PostgreSQL 系库上栽过这个坑——缓存太大OS 缓存被挤占反而开始换页性能更差。最后按经验值调成这样shared_buffers 16GB effective_cache_size 48GB work_mem 64MB maintenance_work_mem 2GB checkpoint_completion_target 0.9改完 reload缓冲池命中率稳定在 92% 以上。顺手查了一眼SELECTround(sum(blks_hit)*100.0/nullif(sum(blks_hit)sum(blks_read),0),2)AShit_ratioFROMsys_stat_database;92.4%不算激进也不算保守刚好。白天救火告一段落又冒出一种怪现象页面点一下等四五秒才有反应但热点 SQL 明明不慢了。我第一反应是锁。查了一下果然有会话在等长事务释放锁SELECTblocked.pidASblocked_pid,blocking.pidASblocking_pid,blocked.locktype,blocked.relation::regclassASrelation,blocked.wait_event_type||:||blocked.wait_eventASwait_eventFROMsys_locks blockedJOINsys_locks blockingONblocking.locktypeblocked.locktypeANDblocking.pidblocked.pidWHERENOTblocked.granted;定位到是应用侧一个没提交的长事务。联系开发改成“用完即提交”排队现象当场消失。这种问题不查锁根本发现不了纯看慢 SQL 会走偏。晚上打了份 KWR 报告把高峰段采样出来复盘。TOP 5 SQL 里前面那条统计报表和 UPDATE 已经被我们压下去剩下的是 UPDATE t_order 和审计写入属于业务本身就重的后面再分批啃。KDDM 我们也跑了一遍相当于让机器帮我们交叉验证。它把我们手工发现的问题又点了一遍缺索引、长事务持锁还顺手确认了 shared_buffers 命中率没问题。机器和人的结论一致心里才真踏实。到周末下午核心接口基本稳了首页报表从 12.4 秒掉到 1.2 秒订单查询从 8.5 秒掉到 0.9 秒统计汇总从 21.3 秒掉到 2.8 秒。CPU 和 I/O 也不再抽风。回头看这套排查路径——连库、抓热点、看计划、改 SQL、建索引、调参数、查锁、KWR、KDDM——在金仓上跑下来和 Oracle 的 AWR/ASH 思路其实是一脉相承的只是工具名字换了。有几个坑当时没写进正式报告但印象很深pg_dump 不带走统计信息迁移完第一件事就是全库 ANALYZE不然优化器是瞎的。参数化查询在金仓上对常量值很敏感ksql 里测的计划和业务绑定变量跑出来的可能不一样。连接池 max_connections 配小了高峰期连接打满请求在应用层排队看起来像数据库慢。KWR 和 KDDM 的权限得提前开不然到时候查不了只能干瞪眼。这些零碎问题单独看都不致命但凑在一起足够把人搞得很狼狈。调优这活很多时候不是靠某个大招而是把这些细节一个一个抠过去。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价