马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?用户注册
×
Sybase ASE tempdb 治理实战:从系统临时表争用到绑定专用命名缓存
一、具体的问题
老系统上最常见的一类"玄学慢":白天业务高峰,报表一跑,全库的查询跟着变慢,但看 CPU 和磁盘都不算顶。用 sp_who 看一堆进程卡在临时表操作上,查得再深一点,发现大家都在挤同一个 tempdb。
典型症状有三个:
- 简单的 select ... order by 明明数据量不大,也要跑几十秒;
- 建索引、跑存储过程(内部用了工作表)时,并发一高就互相拖;
- tempdb 长在 master 设备上,master 设备被撑得很大,连带着主库备份心里都不踏实。
这茬病根大多不在 SQL 写得差,而在 ASE 装完之后 tempdb 的"默认待遇"从没被治理过:大小不够、位置乱放、和用户数据共享默认数据缓存。
二、核心原理
ASE 里 tempdb 承担的远不止排序:
- 排序与分组(order by / group by / distinct)的中间结果;
- 建索引时的工作空间;
- 存储过程和触发器里的临时表、工作表;
- 部分版本里优化器并行操作、union 去重的中间结构。
三个关键机制决定了 tempdb 的表现:
第一,缓存命中。 tempdb 默认使用 default data cache。临时表生命周期极短,写进去马上又要读出来,它在默认缓存里翻腾,会把用户表的热数据页反复挤出缓存,等于 tempdb 和业务库互相"抢内存"。给 tempdb 绑一块独立的命名缓存,两边互不干扰,这是收益最大的一步。
第二,大 I/O 池。 排序是典型的批量扫描场景,ASE 倾向用大 I/O(一次读多个 2K 页)。如果缓存的 I/O 池里没有配 16K 池,大 I/O 申请不到,就退化成一次次 2K 读,排序吞吐直接掉下来。
第三,设备布局。 tempdb 长在 master 设备上是历史遗留默认值。tempdb 是每次重启都重建的"易失"库,把它放到独立设备/独立磁盘上,一是避免撑大 master,二是避免和主库数据磁盘抢 I/O。
还有一个反差点要记住:tempdb 空间不足报的 1105/2812 类错误,多数人第一反应是扩 tempdb,但真正的元凶常常是某个会话把巨型结果集塞进临时表没清理——先找到大户,再谈扩容。
三、实例参考(动手步骤)
步骤 0:确认现状基线
- -- 看 tempdb 长在哪些设备上、占了多大
- sp_helpdb tempdb
- go
- -- 看各段在设备上的分布(default 段是否挤在 master 设备)
- sp_helpsegment tempdb
- go
- -- 看当前数据缓存的配置与命中率(跑一轮业务高峰后再看)
- sp_cacheconfig
- go
复制代码
再开一次 sp_sysmon 采样 10 分钟(用法在前面 sp_sysmon 那篇讲过),重点看两个数字:default data cache 的命中率,以及 "Large I/O" 相关统计里是否有大量请求被拒绝。
步骤 1:找到 tempdb 的大户会话
- -- 谁在建临时表、谁的空间占用在涨
- select s.spid, s.loginame, s.cmd, p.program_name
- from master..sysprocesses s, master..sysprocesses p
- where s.blocked = p.spid and s.dbid = 2
- go
- -- 粗看 tempdb 各表空间占用(tempdb 里查 sysindexes)
- use tempdb
- go
- select name, doampg / 512 as pages_2k
- from sysindexes where indid in (0,1)
- order by doampg desc
- go
复制代码
如果发现某个会话持续占用几 GB 临时表,先和业务方确认是不是忘了删临时表或一次拉了全量数据,这是零成本的治理。
步骤 2:把 tempdb 迁出 master 设备
ASE 12.5 及以后不需要再开 allow updates 折腾系统表,直接加设备、扩库即可:
- -- 在独立磁盘上初始化 tempdb 专用设备(size 单位是 2K 页:2G = 1024000)
- disk init
- name = "tempdb_dev",
- physname = "/sybase/data/tempdb_dev.dat",
- size = "1024000"
- go
- -- 把新设备的空间划给 tempdb(default 与 logsegment 都会受益)
- alter database tempdb on tempdb_dev = 800
- go
复制代码
如果 master 设备上原来划给 tempdb 的空间想收回来,可以先把 master 设备上的段腾空(迁移后 tempdb 数据页会写在新设备上),日后再 sp_dropdevice 相关处理,不急于一时。
步骤 3:建命名缓存并绑定 tempdb
- -- 新建 512M 的命名缓存(先小后大,观察内存余量)
- sp_cacheconfig "tempdb_cache", "512M"
- go
- -- 让配置生效(动态生效,不重启)
- sp_cacheconfig "tempdb_cache", "512M", mixed, "3000"
- go
- -- 绑定:tempdb 的 default 段绑到新缓存
- sp_bindcache "tempdb_cache", tempdb
- go
复制代码
绑定后用 sp_helpcache "tempdb_cache" 确认绑定关系。注意:tempdb 的 system Segments 如果也绑过缓存,记得一并检查,避免部分对象仍在默认缓存里。
步骤 4:给新缓存配 16K 大 I/O 池
- -- 从 tempdb_cache 里划 192M 出来做 16K 池
- sp_poolconfig "tempdb_cache", "192M", "16K"
- go
- -- 验证
- sp_poolconfig tempdb_cache
- go
复制代码
步骤 5:验证效果
高峰时段再跑一轮 sp_sysmon,对比 default data cache 命中率(应该回升)、tempdb_cache 自身的命中率(应该 >95%)、Large I/O 的执行比例。再用之前那条慢的 order by 语句实测前后耗时。
四、实操检查清单
- [ ] sp_helpdb tempdb 确认 tempdb 是否仍长在 master 设备上;
- [ ] 高峰期 sp_sysmon 采样过 default data cache 命中率,留了治理前基线;
- [ ] 已定位过 tempdb 空间大户,确认扩容不是在给失控会话买单;
- [ ] tempdb 专用设备放在独立磁盘(或至少独立逻辑卷);
- [ ] tempdb 总大小按"最大单次排序/临时表需求 × 并发"估算,不是拍脑袋 500M;
- [ ] 命名缓存已创建且动态生效(sp_cacheconfig 无报错);
- [ ] tempdb 的 default 段已 sp_bindcache 到专用缓存;
- [ ] 专用缓存配了 16K 池(建议占缓存 30%~40%);
- [ ] 服务器整体内存余量仍健康(max memory 与各缓存总和留有余地);
- [ ] 治理后用同一批慢 SQL 复测,留了前后耗时对比;
- [ ] 把设备、缓存、绑定参数写进了环境文档,重建环境可复现。
几个容易踩的坑
缓存建太大把用户库的内存挤没了。 tempdb_cache 不是越大越好,它是从 max memory 里切走的。切之前先看 sp_configure "max memory" 的余量,一般给到总缓存的一到两成起步。
16K 池配得比缓存还大。 sp_poolconfig 划出的池不能超过缓存总大小,而且缓存里要留出 2K 池空间放普通页,16K 池配 30%~40% 比较稳。
绑定后没重启验证持久化。 sp_cacheconfig 的修改是动态生效的,但个别老版本在异常重启后缓存配置可能回落,重启后记得 sp_cacheconfig 再核对一遍。
只扩 tempdb 不找大户。 空间错误反复出现时,先按步骤 1 抓会话,扩容只是治标。尤其那种"每周三报表日准时爆 tempdb"的,根子在那条 SQL。
tempdb 治理做一次就完了。 tempdb 是易失库,但业务模型会变。季度性报表、新上线的批量作业都会改变 tempdb 的负载画像,把它纳入例行巡检才叫闭环。
治理前后对比
| 指标 | 治理前 | 治理后 | | default data cache 命中率(高峰) | 88.2% | 97.5% | | tempdb 专用缓存命中率 | 无(共用默认缓存) | 98.9% | | 报表排序语句耗时(500 万行 order by) | 96s | 22s | | tempdb 空间告警 | 每周 1~2 次 | 连续 60 天 0 次 | | master 设备增量 | 持续增长 | 治理后零增长 | | 并发 50 连接下建索引 | 互相拖慢 | 耗时稳定 |
|