前事不忘,后事之师,不忘国耻!

 找回密码
 用户注册
搜索
查看: 21|回复: 0

[开发应用] Sybase ASE 内存与过程缓存治理实战:从 701 报错和 71% 命中率到命名缓存拆分

[复制链接]
发表于 18 小时前 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?用户注册

×
一、具体的问题

一套 ASE 15.7 业务库,白天接交易、晚上跑报表和批量结算,两拨负载挤在同一实例里。问题攒了两个月,攒成三条:


  • 每天 02:10 左右的结算批处理零星报错,错误是 701,severity 17:

  1. Error: 701, Severity: 17, State: 12
  2. There is not enough procedure cache to run this procedure, trigger, or SQL batch.
  3. Retry later, or ask your SA to reconfigure Adaptive Server with more procedure cache.
复制代码

重跑往往能过,运维加了自动重试,这条错误被"消化"掉,两个月没人管。


  • 白天报表那批 SQL 一上来,同一张订单表上的主键查询 P95 从 30ms 抖到 1.8 秒。
  • 监控上物理读尖峰、io busy 峰值 92%,而 CPU 一直很闲。


运维的第一反应很标准:加内存。max memory 32G 提到 64G,重启,现象一模一样:物理读没降,P95 照抖,701 每天来。

真正的发现在 sp_cacheconfig 的输出上:
  1. Cache Name            Status    Type      Config Size   Run Size
  2. default data cache    Active    Default   0.00 Mb       8.00 Mb
复制代码

Config Size 是 0.00 Mb。这台机器的 default data cache 从上线到现在从来没有被显式配置过,一直在用系统给的默认小值。max memory 翻了一倍,数据缓存一个字节都没多——ASE 不会因为你把仓库租大了,就自动帮你把货架摆出来。

过程缓存那边,procedure cache size 配的是 102400 页(约 200MB);批量窗口里 Max_Used 顶到配置值,Reuse_cnt 累计两万六千多——不是偶尔紧张,是长期满负荷复用。

所以问题不是内存不够,是三件事叠在一起:数据缓存没配、只有单页 IO 池导致范围扫描把缓冲冲干净、过程缓存按"感觉"给的 200MB 而不是按编译峰值定的。加内存解决不了其中任何一个。

二、核心原理

ASE 的内存是分层的:操作系统共享内存由 max memory 划定上限,之下分给 default data cache、各命名缓存、过程缓存、语句缓存、worker process、锁与连接结构。所有缓存配置值之和(total logical memory)必须小于 max memory,否则实例起不来。sp_helpcache 的 Memory Available 就是"还能划出多少"。

default data cache 必须显式给绝对值。 它不会自动跟随 max memory。不写 sp_cacheconfig "default data cache", "24G",它就一直是默认小值。这是 ASE 上最常见的一种"配了等于没配"。

改尺寸要看两列。 Config Size 是配置文件里的值,Run Size 是当前运行值。改尺寸要重启才生效,两列不一致就是改了没重启。

wash size 决定脏页什么时候被赶出去。 默认按池的 20% 算(池小于 300MB),超过 300MB 则按 300MB 对应缓冲数折算。太小则脏页积压到临界才集中写盘,checkpoint 期间 IO 打满;太大则可用缓冲变少。

大 IO 池决定范围扫描的效率。 只有逻辑页大小的池(2K 或 4K),所有 IO 都单页往返,预读能力等于没有。建 16K 池,逻辑页 2K 的服务器一次 IO 就是连续 8 页。

建池在线生效,但不一定到位。 sp_poolconfig 运行时能执行,可它只能搬空闲缓冲(没被占用、没有未写盘脏页的),所以常提示 requested 与 actual 不一致,得重启后才到位。

命名缓存是隔离,不是加速。 把热表和热索引绑到独立缓存,本质是把它们从"和大表扫描共用一个缓冲池"里捞出来。绑定粒度可到表,也可到索引,在线生效。

过程缓存不只是存存储过程文本。 它还装查询计划、编译与优化工作区、部分执行期排序区;开了语句缓存,缓存语句也吃这块。所以需求跟"并发编译峰值"走,而不是跟"存了多少存储过程"走。ASE 15 之后计划普遍更大,从 12.5 迁上来通常要给到原来的 2 到 6 倍。

701 不代表物理内存不足。 它的含义是"这一次分配不到"。一条特别大的 SQL 单独编译就可能失败,同时其它小语句照跑;平时能跑的存储过程,在 schema 变更触发重编译的那次也可能突然要一大块。

一句话:加内存是给仓库扩容,配缓存是把货架分区。只扩容不分区,热页照样被冲刷。

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

步骤 0:确认现状基线

改动之前把六条命令的原样输出存下来,这是后面所有对比的基准,也是出事时唯一能自证的东西。
  1. sp_configure "max memory"
  2. go
  3. sp_configure "allocate max shared mem"
  4. go
  5. sp_configure "procedure cache size"
  6. go
  7. sp_cacheconfig
  8. go
  9. sp_helpcache
  10. go
  11. sp_monitorconfig "procedure cache size"
  12. go
复制代码

判读三条:Config Size 是不是 0.00 Mb(是则从未配置);Config 与 Run 是否一致(不一致则改过未重启);Memory Available 还剩多少。案例基线:max memory 32G,default data cache 仅 8MB 级,无 16K 池、无命名缓存,procedure cache size 102400 页。

步骤 1:先量需求,再动配置

数据缓存的需求看命中率与物理读。挑报表高峰窗口采样十分钟:
  1. sp_sysmon "00:10:00", dcache
  2. go
复制代码

重点看 Data Cache Management 段的 Cache Hits 与 Physical Reads、Buffer Wash 段的 wash 次数。命中率低于 90%、物理读随报表上涨,说明热数据被扫描冲走——这种情况才可能靠"分区"改善,而不是靠加总容量。

过程缓存的需求看编译峰值:
  1. sp_monitorconfig "procedure cache size"
  2. go
  3. select * from monProcedureCacheMemoryUsage
  4. go
  5. select * from monProcedureCacheModuleUsage
  6. go
复制代码

Max_Used 是历史高水位,Reuse_cnt(部分版本显示 Num_Reuse)非 0 且持续增长就是确凿的短缺信号。后两张 MDA 表用来看哪个模块的编译把缓存吃掉了——案例里是报表那批拼接出来的大 SQL,单条编译要四十多兆。

步骤 2:显式配置 default data cache 与大 IO 池
  1. -- max memory 单位是 2K 页:40GB = 40 * 1024 * 1024 / 2 = 20971520 页
  2. sp_configure "max memory", 20971520
  3. go
  4. sp_configure "allocate max shared mem", 1
  5. go
  6. -- 显式给默认数据缓存定绝对值(不是 0.00 Mb),24GB
  7. sp_cacheconfig "default data cache", "24G"
  8. go
  9. -- 划出 6GB 的 16K 池给范围扫描与排序
  10. sp_poolconfig "default data cache", "6G", "16K"
  11. go
复制代码

执行完必须重启,再跑 sp_cacheconfig 确认 Config Size 与 Run Size 都是 24.00 Gb、池里出现 6Gb 的 16K 行。若当时提示的 actual 小于 requested,重启后重跑一次即可到位。

留内存的原则:total logical memory 别顶到 max memory 的 100%,留 10% 到 15% 给 worker process、锁结构、连接内存这类会波动的消费者,也给操作系统留余量。案例最终 max memory 40G、default data cache 24G、命名缓存 6G、过程缓存 512M。

步骤 3:把热表隔离到命名缓存
  1. sp_cacheconfig "cache_hot", "6G"
  2. go
  3. sp_poolconfig "cache_hot", "1G", "16K"
  4. go
  5. sp_bindcache "cache_hot", "bizdb", "t_order"
  6. go
  7. sp_bindcache "cache_hot", "bizdb", "t_order", "ix_order_cust"
  8. go
  9. sp_helpcache "cache_hot"
  10. go
复制代码

容量按"热数据量 × 1.2"估,多出的 20% 给 wash 留余量。绑定在线生效,sp_helpcache 末尾会列出对象与索引——只绑表不绑索引是最常见的半成品。反过来,大表全表扫描的对象不要绑到热缓存,那等于把刚隔离好的热页重新冲一遍。

步骤 4:过程缓存按峰值定容
  1. -- 262144 页 × 2K = 512MB
  2. sp_configure "procedure cache size", 262144
  3. go
复制代码

这个参数是动态的,改完不用重启。增量别一步到位,按官方建议的量级走:1GB 以下每次加 128MB,1GB 到 2GB 每次加 256MB,往上类推。

调完在下一个批量窗口再跑 sp_monitorconfig,验收三条:Pct_act 峰值不再顶到 100%;Max_Used 稳定在配置值的 60% 到 75%(低了说明配多,可回收给数据缓存);Reuse_cnt 保持 0 或极小值。案例提到 512MB 后落在 68%,Reuse_cnt 归零。

步骤 5:把 701 纳入可统计范围

701 默认只发给客户端,errorlog 里看不到,所以频次永远是"听说":
  1. sp_altermessage 701, 'with_log', true
  2. go
复制代码

如果加完缓存 701 依然出现,说明是单条巨无霸 SQL 的编译需求问题,抓现场:
  1. sp_configure 'dump on conditions', 1
  2. go
  3. sp_shmdumpconfig 'add', 'error', 701, 1, '/sybase/dump', null, include_proc
  4. go
  5. sp_shmdumpconfig
  6. go
复制代码

取证完记得 sp_shmdumpconfig 'drop', 'error', 701 关掉,转储文件很大。

治理前后对比

指标治理前治理后
default data cache 配置值0.00 Mb(未配置)24 Gb
16K 大 IO 池无6 Gb
缓存命中率(报表时段)71.4%98.6%
物理读峰值4,200 次/秒310 次/秒
主键查询 P95(报表并发时)1.8 秒42 毫秒
701 报错频次37 次/天0(连续三周)
过程缓存 Max_Used顶格 100%68%
过程缓存 Reuse_cnt26,4800
结算批处理总时长3 小时 52 分2 小时 06 分
io busy 峰值92%41%


批处理时长缩了一半,很大一部分来自 16K 池——那张两千万行的结算表原来每次都是单页 IO 一点点啃。

四、实操检查清单


  • [ ] default data cache 的 Config Size 是显式绝对值,不是 0.00 Mb
  • [ ] 每个缓存 Config Size 与 Run Size 一致(不一致 = 改了没重启,此刻没生效)
  • [ ] Memory Available 不为负,total logical memory 占 max memory 的 85% 以内
  • [ ] 存在与负载匹配的大 IO 池(2K 页服务器至少一个 16K 池),尺寸与扫描量级相当
  • [ ] wash size 核过,大池的 wash 不会导致 checkpoint 期间 IO 集中
  • [ ] 热表、热索引绑到独立命名缓存,容量按热数据 × 1.2 估算
  • [ ] Cache Binding Information 里能看到索引级绑定,不止表级
  • [ ] statement cache size 若已开启,占用算进过程缓存预算(两者共用一块)
  • [ ] procedure cache size 按批量窗口的 Max_Used 定容,不是按 max memory 百分比拍脑袋
  • [ ] Reuse_cnt 长期为 0
  • [ ] sp_altermessage 701, 'with_log', true 已执行,errorlog 能统计频次
  • [ ] 每次改配置都有"重启后复核 Config/Run 双列并存档"的固定动作


几个容易踩的坑

以为加了 max memory 数据缓存就大了。 default data cache 不显式配置就一直是默认小值,加到 64G 那一刻命中率一个点都没动——本案例白折腾两个月就栽在这。

看到 Config 改了、池子也没报错,就当生效。 Config 24G / Run 8M 的组合同样常见;sp_poolconfig 在线执行只能搬空闲缓冲,requested 与 actual 不一致是常态。改尺寸必须重启,复查必须看 Run 列。

只绑表不绑索引。 热表进了新缓存,索引 IO 还在老池子里跟报表扫描抢缓冲,改善幅度只有预期的一半。

过程缓存一路加。 它和数据缓存抢同一个 max memory。见过过程缓存配到 4G 而数据缓存只有 2G 的实例,编译不报错了,查询全在等 IO。

只盯总量不看单条。 701 也可能是某条拼接出来的大 SQL 编译就要几十兆,加总容量只是降低发生概率,根子在那条 SQL 的写法(去嵌套、拆临时表、避免巨型 union)。

没给操作系统留余量。 max memory 贴到物理内存 100%、allocate max shared mem 又置 1,操作系统被挤到换页,ASE 反而整体变慢。

701 只在客户端日志里见到。 没开 with_log,errorlog 里查不到,就没法统计频次、没法判断治理有没有效果,只能靠"最近好像没听说"这种结论。
免责申明1、欢迎访问本站,本文内容及相关资源来源于网络,版权归版权方所有!本站原创内容版权归本站所有,请勿转载!
2、本文内容仅代表作者观点,不代表本站立场,作者自负,本站资源仅供学习研究,请勿非法使用,否则后果自负!请下载后24小时内删除!
3、本文内容,包括但不限于源码、文字、图片等,仅供参考。本站不对其安全性,正确性等作出保证。但本站会尽量审核会员发表的内容。
4、如本帖侵犯到任何版权问题,请立即告知本站 ,本站将及时删除并致以最深的歉意!客服邮箱:admin@dbabbs.com
您需要登录后才可以回帖 登录 | 用户注册

本版积分规则

Archiver|小黑屋|DBA论坛中国 ( 鲁ICP备20017503号-2 )

GMT+8, 2026-11-8 23:59 , Processed in 0.028665 second(s), 9 queries , MemCached On.

Powered by Discuz! X5.0

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表