我们的文章会在微信公众号IT民工的龙马人生和博客网站 ( www.htz.pw )同步更新 ,欢迎关注收藏,也欢迎大家转载,但是请在文章开始地方标注文章出处,谢谢!
由于博客中有大量代码,通过页面浏览效果更佳。
YashanDB 一把定位引起SWAP空间异常的会话和SQL语句
摘要
在 Oracle 运维时,TEMP 被打爆、排序/哈希往外换,路径几乎不用想。到了 YashanDB,TEMP 表空间还在,但 VM 中间结果换出主要落在 SWAP,关键参数也变成以 VM_BUFFER_SIZE 为代表的一套——不能再用 PGA + TEMP 的老地图硬套。
SWAP 水位往上走,或会话卡在 swapping * vm block,业务侧往往就是突然变慢。这时不必先手写拼 V$VM / V$VMSTAT 那一串:先 ytop -f swap.sql 把参数、页、水位、段和会话收齐,抄到 sql_id 再 ytop -f sql.sql 看文本和计划。
文中两例都在 23.5.2.101 上压过:临时 LOB,以及 HASH JOIN 把 vm_buffer 打穿。第二例里段类型有时会显示成 Unknown,别只盯着手册上的字面量 HASH。
1. 在 Oracle 运维时,TEMP 几乎是肌肉记忆
大排序(ORDER BY / GROUP BY)、哈希连接溢出、建索引排序、部分物化,中间结果放不下 PGA,就会落到 TEMP。值班常见路径是 dba_temp_free_space、v$tempseg_usage、v$sort_usage,再追到会话和 SQL;调参也绕不开 PGA 和 TEMP 文件——通常先点名 SQL,再谈加 TEMP。
做 YashanDB 时,这套直觉还在,只是落盘的主战场换了名字和参数。
2. YashanDB:还有 TEMP,主力却是 SWAP
看 dba_temp_free_space,多数实例里 TEMP 和 SWAP 都会在:
| 名称 | 在 YashanDB 里大致干什么 | 别和谁搞混 |
|---|---|---|
| TEMP 表空间 | 仍保留;部分临时对象/路径会用到 | 不要默认“和 Oracle TEMP 一一对应、扛全部溢出” |
| SWAP 表空间 | VM(虚拟内存机制)在 vm_buffer 不够时的磁盘换出区 |
Linux swap 分区/文件 |
VM / vm_buffer |
排序、HASH、物化、临时 LOB 等中间结果优先吃的内存 | DATA_BUFFER_SIZE、OS 虚拟内存总称 |
| 关键参数 | 常见盯 VM_BUFFER_SIZE(以及版本手册里的相关 VM 参数) |
不要照搬 Oracle 的 PGA_AGGREGATE_TARGET 心智直接套 |
结构上可以看成:
中间结果(VM)
├─ vm_buffer(内存,优先)
└─ SWAP 表空间(buffer 不够再换出)
SWAP 水位涨、会话卡在 swapping * vm block,体感和当年 TEMP 被打爆差不多,只是对象变成了 SWAP 和 VM 相关视图。
OS 内存和磁盘 IO 可以对照看,但库内 SWAP 换出不等于操作系统 swap 在用。
3. 哪些场景会吃 SWAP,优先怎么处理
常见会把 VM 顶高、再打到 SWAP 的场景,和当年 TEMP 压力来源差不多:
- 大数据量
ORDER BY/ 多级排序、缺过滤或排序列吃不到索引。 - 大数据量
HASH JOIN:建哈希表溢出。 - 统计信息收集(采样与中间结果),尤其挤在业务高峰。
- 建/重建索引时的键排序。
- 执行算子物化(复杂 SQL、多阶段、大结果集)。
- 临时 LOB:拼接/转换、
DBMS_LOB.CREATETEMPORARY、表达式产生的 TempLOB;NOCACHE更容易往 SWAP 的LOB_DATA落。
VM/SWAP 高不等于一定是内存参数配错,也可能是计划差、高峰撞车、临时 LOB 没释放、或中间结果本身就大。
处理时建议先软后硬:
| 先后 | 做什么 | 说明 |
|---|---|---|
| 1 | 优化 SQL / 错峰 | 减排序与 HASH 规模;大统计、建索引、批量 LOB 避开高峰 |
| 2 | 查临时 LOB 生命周期 | 用完是否释放;连接池长会话是否越积越多;是否不必要的 NOCACHE |
| 3 | 再评估 VM_BUFFER_SIZE |
确认 SQL/调度/LOB 合理后再改;多为 SPFILE + 重启类变更,先点名再动刀 |
| 4 | 再评估 SWAP 扩容 | 只缓解容量与 IO 顶死;不能替代 SQL/LOB 治理 |
也常见这些坑:一见换出就扩容;把 SWAP 高当成内存泄漏;只看全局不看会话;采一次样就断定 LOB 泄漏;没有 LOB_DATA 仍硬扣 LOB;没跟业务确认就杀会话或改参数。
视图当然可以手查,更省事的是:
ytop -f swap.sql → ytop -f sql.sql
4. 先跑 swap.sql
SWAP 告警或变慢时,一条命令往往就够:
ytop -f swap.sql
输出里按段看:
| 段 | 看什么 | 异常时常见样子 |
|---|---|---|
| 1 参数 | VM_BUFFER_SIZE 等 |
buffer 是否过小 |
2 GV$VM |
FREE / SWOUT / FSWAP |
FREE≈0 且 SWOUT 上升 |
| 3 水位 | SWAP / TEMP 的 USED_PCT | SWAP 持续上涨 |
| 4 段+LOB+会话 | TS=SWAP 的 MB、SEGTYPE、nc、sql_id |
谁在占 SWAP |
| 5 VM 会话 | cswo / swo / event |
谁在猛换页(未必已有大段) |
抄到 sql_id 后再:
ytop -f sql.sql
远端可用 ytop -t <host> -f swap.sql,或在库机直接跑。
5. 案例一:临时 LOB 顶高 SWAP
先看全局页和水位:
ytop -f swap.sql
I TOTAL FREE OPENED CLOSED SWOUT CTRL FSWAP
-- ---------- ---------- ---------- ---------- ---------- ---------- ----------
1 21196 235 444 20515 6722 2 0
TABLESPACE_NAME SIZE_MB FREE_MB USED_MB USED_PCT
---------------- ---------- ---------- ---------- --------
SWAP 2432 2011 421 17.3
TEMP 64 59 5 7.8
SWOUT=6722,SWAP 已用约 421MB(17.3%)。FREE 还剩一点,说明 buffer 紧,而且已经往 SWAP 换了。
再看谁在换页:
ytop -f swap.sql
SID_TID EVENT USERNAME SQL_ID EXECT PROGRAM COPN CSWO SWO SWI ALC IOW EXT
------------------ ---------------------- ---------- --------------- ------ -------------- ------ ------ -------- -------- -------- -------- ------
1.44.30.880659 SYS .dbnkcw6u8j20u 1.17M /data/yashan/y 0 2.7K 4K 0 35.8K 0 4K
1.46.25.880661 SYS .dbnkcw6u8j20u 1.17M /data/yashan/y 0 4K 1.5K 0 27.4K 0 1.5K
同一条 dbnkcw6u8j20u,CSWO 在几千。SQL_ID 列有时带 SEL. 前缀还会截断,抄工单时用完整 sql_id。
临时段也对得上:
ytop -f swap.sql
USERNAME SID_TID SQL_ID TS SEGTYPE MB EXTENT CA NC AB EVENT
------------ ---------------- --------------- -------- ---------- ------- ------ ---- ----- ---- ------------------
SYS 1.46.25 dbnkcw6u8j20u SWAP LOB_DATA 31.69 3985 0 800 0
SYS 1.44.30 dbnkcw6u8j20u SWAP LOB_DATA 21.78 2737 0 800 0
SID 44/46,SWAP 上 LOB_DATA,大约 22~32MB,同行列 nc=800(NOCACHE)且不掉。文本侧是循环 DBMS_LOB.CREATETEMPORARY(..., FALSE)(nocache),对临时 LOB 反复 WRITEAPPEND 还长期持着。ca(CACHE)多压 vm_buffer,nc 更容易落到 SWAP 的 LOB_DATA。
这条线干净:LOB → LOB_DATA@SWAP → 高 nc → 高 CSWO。
6. 案例二:HASH JOIN 打穿 vm_buffer,SEGTYPE 却是 Unknown
手册里 SEGTYPE 有 HASH 这一项,实际踩坑时有两点要注意:
vm_buffer还宽裕时,会话侧可能已经有换出计数,临时段却长时间没有行——别因此放过。- buffer 打穿、SWAP 水位上来以后,段会出现,但类型未必显示成
HASH。
把 buffer 收紧、HASH 工作集做大以后:
ytop -f swap.sql
I TOTAL FREE OPENED CLOSED SWOUT CTRL FSWAP
-- ---------- ---------- ---------- ---------- ---------- ---------- ----------
1 2046 0 1050 986 5357 9 14788
TABLESPACE_NAME SIZE_MB FREE_MB USED_MB USED_PCT
---------------- ---------- ---------- ---------- --------
SWAP 2432 1172 1260 51.8
TEMP 64 59 5 7.8
FREE=0,SWOUT=5357,SWAP 用到约 1260MB(51.8%)。FSWAP=14788。
会话上的 event 已经写明白了:
ytop -f swap.sql
SID_TID EVENT USERNAME SQL_ID EXECT PROGRAM COPN CSWO SWO SWI ALC IOW EXT
------------------ ---------------------- ---------- --------------- ------ -------------- ------ ------ -------- -------- -------- -------- ------
1.38.1266.896498 swapping out vm block SYS SEL.3n81f1dnhkh 9.46S /data/yashan/y 1.1K 5.4K 305.7K 300.4K 8.7K 50 6.7K
SWO/SWI 到了 30 万级,IOW=50。sql_id 完整是 3n81f1dnhkhkv。
临时段有行,但类型是 Unknown:
ytop -f swap.sql
USERNAME SID_TID SQL_ID TS SEGTYPE MB EVENT PROGRAM
------------ ---------------- --------------- ---------- ------------ -------- -------------------- --------------
SYS 1.38.1266 3n81f1dnhkhkv SWAP Unknown 41.86 swapping in vm block /data/yashan/y
再看文本和计划:
ytop -f sql.sql
# sql_id = 3n81f1dnhkhkv
文本样例:
SELECT /*+ USE_HASH(a b) FULL(a) FULL(b) */ COUNT(*)
FROM ... a, ... b
WHERE a.c2 = b.c2
AND a.id <> b.id
计划样例:
Plan Hash Value: 3673659905
|Id|Operation |Name |
|--|--------------------------|------------|
| 0|SELECT STATEMENT | |
| 1| AGGREGATE | |
| 2| HASH JOIN INNER | |
| 3| TABLE ACCESS FULL |... (A) |
| 4| HASH GROUP | |
| 5| TABLE ACCESS FULL |... (B) |
合在一起看:Unknown@SWAP、swapping * vm block、计划又是 HASH JOIN,就按 HASH 换出处理,别因为 SEGTYPE 不是 HASH 就停。Unknown 同样要当换出段看。
SWOUT=0 也不等于没事:会话侧分配/打开计数可能已经很高,只是还没换出去。
7. 两条线对照
| 项 | LOB | HASH(打穿后) |
|---|---|---|
| sql_id | dbnkcw6u8j20u |
3n81f1dnhkhkv |
| 段类型 | LOB_DATA@SWAP |
Unknown@SWAP |
| 会话换出 | 高 CSWO |
event=swapping out vm block,SWO/SWI 很高 |
| 文本/计划 | nocache 临时 LOB | HASH JOIN + 双侧全表 |
| 全局 | SWOUT=6722,SWAP≈421MB | FREE=0,SWOUT=5357,SWAP≈1260MB |
若要看历史等待或 TOP SQL,可以再叠 ASH 相关脚本(前提是库上 ASH 开着)。
8. 建议
- 告警先跑
swap.sql,把参数、FREE/SWOUT、水位、段和会话一次看完。 - 盯
swapping * vm block以及CSWO/SWO/IOW/ALC,抄完整 sql_id。 - 见到
LOB_DATA按临时 LOB 查;见到Unknown@SWAP 结合计划判断是不是 HASH/中间结果换出。段暂时没有行,也不要放过会话侧的换出计数。 - 拿到 sql_id 后跑
sql.sql,先定性 HASH / SORT / LOB,再谈改写、限流或扩容。 - 库内 SWAP 和操作系统 swap 不是一回事;需要时用
sar/iostat对照磁盘。 - 连续采两三次,看
SWOUT、LOB 和临时段有没有回落。 - 定位清楚再动
VM_BUFFER_SIZE或扩 SWAP。内存参数往往要改配置并重启才能缩小;先改参数容易把根因盖住。
9. 总结
SWAP 异常时,不必从零拼动态性能视图。常用路径:
ytop -f swap.sql → 抄 sql_id → ytop -f sql.sql。
临时 LOB 一侧,多见 LOB_DATA@SWAP,再加高 nc、高 CSWO。HASH 一侧,buffer 打穿后换出很猛,段类型却可能是 Unknown,要靠计划和 swapping * vm block 认。
先点名,再优化。


YashanDB 一把定位引起SWAP空间异常的会话和SQL语句:等您坐沙发呢!