当前位置: 首页 > YashanDB, 优化, 故障 > 正文

我们的文章会在微信公众号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 压力来源差不多:

  1. 大数据量 ORDER BY / 多级排序、缺过滤或排序列吃不到索引。
  2. 大数据量 HASH JOIN:建哈希表溢出。
  3. 统计信息收集(采样与中间结果),尤其挤在业务高峰。
  4. 建/重建索引时的键排序。
  5. 执行算子物化(复杂 SQL、多阶段、大结果集)。
  6. 临时 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 这一项,实际踩坑时有两点要注意:

  1. vm_buffer 还宽裕时,会话侧可能已经有换出计数,临时段却长时间没有行——别因此放过。
  2. 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. 建议

  1. 告警先跑 swap.sql,把参数、FREE/SWOUT、水位、段和会话一次看完。
  2. 盯 swapping * vm block 以及 CSWO/SWO/IOW/ALC,抄完整 sql_id。
  3. 见到 LOB_DATA 按临时 LOB 查;见到 Unknown@SWAP 结合计划判断是不是 HASH/中间结果换出。段暂时没有行,也不要放过会话侧的换出计数。
  4. 拿到 sql_id 后跑 sql.sql,先定性 HASH / SORT / LOB,再谈改写、限流或扩容。
  5. 库内 SWAP 和操作系统 swap 不是一回事;需要时用 sar / iostat 对照磁盘。
  6. 连续采两三次,看 SWOUT、LOB 和临时段有没有回落。
  7. 定位清楚再动 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语句:等您坐沙发呢!

发表评论

gravatar

? razz sad evil ! smile oops grin eek shock ??? cool lol mad twisted roll wink idea arrow neutral cry mrgreen

快捷键:Ctrl+Enter