临时表空间不存用户数据,存的是「中间结果」:排序、哈希连接、临时表。
排序先在内存(PGA)做,放不下才落盘,落盘就落到临时表空间。
它不走 redo、不走 undo,所以崩溃后内容直接丢弃。
报错是 ORA-01652(unable to extend temp segment)。
8.5 讲 undo(改之前的样子),这一篇讲 temp(算到一半的样子)。 两者都是「辅助空间」,但一个要恢复、一个不用恢复—— 这个差别决定了它们的备份策略完全不同。 临时表空间不够是「查询失败」的常见原因之一,而且报错信息很容易被误读成「表空间满了」。
一、简介:三种场合会用到它
| 场合 | 什么时候落盘 | 典型语句 |
|---|---|---|
| 排序 | 内存排序区放不下 | order by/group by/distinct |
| 哈希连接 | 哈希表放不下 | 大表 join 小内存 |
| 临时表 | 一直放着 | CREATE GLOBAL TEMPORARY TABLE |
排序先在 PGA 的排序区做(SORT_AREA_SIZE 或自动管理的 PGA_AGGREGATE_TARGET)。
放不下就「分块排、写盘、归并」——这就是外部排序,和算法课讲的一模一样。
临时表空间里的东西不写 redo、不写 undo。
所以实例崩溃之后,里面的内容直接丢弃,重开库时重新初始化。
这也是为什么它「不用备份」——备份一个「算到一半的中间结果」没有意义。
二、创建:用 TEMPFILE 不是 DATAFILE
关键差别:临时表空间用的是 TEMPFILE,不是 DATAFILE。
所以查它的视图是 V$TEMPFILE/DBA_TEMP_FILES,不是 V$DATAFILE。
用 V$DATAFILE 查临时文件,一行都查不到——这一点最容易搞混。
建临时表空间时 EXTENT MANAGEMENT LOCAL UNIFORM SIZE 1M 是标准写法。
临时段的大小是固定的,用 UNIFORM 更好预测——
反正里面不留长期数据,不需要 AUTOALLOCATE 那种渐进策略。
临时表空间不能建永久对象。
CREATE TABLE ... TABLESPACE temp 直接报错——
因为它的内容不写 redo、不保证持久,放永久数据等于自毁。
三、查询:三张视图看明白
DBA_TEMP_FILES 看文件:路径、大小、是否自动增长。
V$TEMPFILE 是它的动态版。
V$TEMPSEG_USAGE 看「现在谁在用它、用了多少」——这张最有用,出问题时第一个查它。
V$SORT_USAGE 也能看排序占用,但它只记「正在排的」,历史查不到。
要查历史得靠 AWR 里的 TEMP 相关段。
一个排查技巧:临时表空间「用完了但不释放」是正常现象。
临时段用完会标记为空闲,但文件不缩小。
所以「用了 90%」不一定是问题,要看 V$TEMPSEG_USAGE 里有没有活跃会话。
没有活跃会话还占着,才是真问题(通常是会话没断干净)。
四、临时表空间组:分散,不是均衡
临时表空间组(10g 起)的作用是「轮流分配」:
每个新会话连上来,按顺序分到组里不同的临时表空间。
十个会话同时做大排序,会分散到三个文件上,而不是挤一个。
建组三句:建表空间时带 TABLESPACE GROUP、把已有表空间加入组、把组设为默认临时表空间。
注意组不是「负载均衡」。
它是「轮着来」,不看谁空。
所以可能出现「一个用了 180MB、另一个只用 40MB」的情况——轮到谁就是谁,不回头调整。
什么时候该建组?并发排序多、单个 TEMP 老报 ORA-01652 的库。
治法是「加文件或建组」,别只盯着「把 TEMP 调大」——一个文件太大,争用反而更集中。
五、动手 Demo:算一遍外部排序:一万行分五段落盘
沙箱里没有 Oracle,CREATE TEMPORARY TABLESPACE 执行不了。
但「排序放不下内存会落多少盘」这件事,是一道能算准的题。
我按「一万行、每行 200 字节、排序区只放 2000 行」算了一遍:
分成几段、每段多大、总共落多少盘、并发放大多少倍,最后演一遍临时表空间组怎么轮流分配会话。
MB = 1024 * 1024
rows, rowlen, mem_rows = 10000, 200, 2000
seg = -(-rows // mem_rows)
per_seg = mem_rows * rowlen
print('一万行、每行 %d 字节,排序区只放 %d 行' % (rowlen, mem_rows))
print('分成 %d 段,每段 %d 行、约 %d 字节' % (seg, mem_rows, per_seg))
print('落盘总量约 %.2f MB' % (seg * per_seg / MB))
# 并发放大:100 个会话同时排
for s in (1, 10, 100):
print('%3d 个会话同时排 -> 需要 %.0f MB 临时空间' % (s, seg * per_seg * s / MB))
# 临时表空间组:会话依次分到不同成员
members = ['temp01', 'temp02', 'temp03']
for i in range(6):
print('第 %d 个会话 -> %s' % (i + 1, members[i % len(members)]))
实跑输出:
--------------------------------------------------------------------
第三十八课:临时表空间——排序和哈希的落脚点
--------------------------------------------------------------------
**临时表空间不存用户数据,它存「中间结果」。**
**什么时候会有中间结果?三种场合:排序(`order by`/`distinct`/`group by`)、哈希连接、临时表。**
**排序先在内存里做(`PGA` 的排序区),放不下才落盘。** **落盘落到哪儿?临时表空间。**
**所以「临时表空间不够」的报错是 `ORA-01652`(unable to extend temp segment)**,
**它的表现是「一条查询跑着跑着失败」,而不是「写数据失败」。**
算一下落盘的量
--------------------------------------------------------------------
**一万行、每行 200 字节,排序区只能放 2000 行:**
**于是分成 5 段,每段 2000 行先排好,写到临时表空间。**
**再把这 5 段归并(merge),输出有序结果。**
**落盘总量约 2MB。**
**这就是「外部排序」的经典做法:分块排、写盘、归并。**
**临时表空间要装的就是这 5 段中间结果。**
**注意:如果同时有 100 个会话都在做大排序,需要的临时空间是 2MB 乘 100。**
**这就是为什么临时表空间不能只按「一条语句够用」来给。**
建一个临时表空间
--------------------------------------------------------------------
[Oracle 原码 · 本沙箱不执行]
| CREATE TEMPORARY TABLESPACE temp2
| TEMPFILE '/u01/oradata/ORCL/temp02.dbf'
| SIZE 200M AUTOEXTEND ON NEXT 50M MAXSIZE 2G
| EXTENT MANAGEMENT LOCAL UNIFORM SIZE 1M;
临时表空间的文件叫 TEMPFILE,不是 DATAFILE。
**关键差别:临时表空间用的是 `TEMPFILE`,不是 `DATAFILE`。**
**所以查它的视图是 `v$tempfile`/`dba_temp_files`,不是 `v$datafile`。** **这一点最容易搞混**——
**用 `v$datafile` 查临时文件,一行都查不到。**
**临时表空间不走 redo,也不走 undo。** **所以它里面的东西「不用恢复」**——
**实例崩溃之后临时文件里的内容直接丢弃,重开库时重新初始化。**
**这也是为什么临时表空间可以 `NOLOGGING` 而不影响恢复。**
临时表空间组:让并发排序不挤一个文件
--------------------------------------------------------------------
SQL> select grp as 组, member as 成员, used_mb as 已用MB from tgroup
组 成员 已用MB
--------------------------
TEMP_GRP temp01.dbf 180
TEMP_GRP temp02.dbf 40
TEMP_GRP temp03.dbf 55
已选择 3 行
[Oracle 原码 · 本沙箱不执行]
| CREATE TEMPORARY TABLESPACE temp2 TEMPFILE '...' SIZE 200M
| TABLESPACE GROUP temp_grp;
| ALTER TABLESPACE temp1 TABLESPACE GROUP temp_grp;
| ALTER DATABASE DEFAULT TEMPORARY TABLESPACE temp_grp;
**组的作用是「轮流分配」:** **每个新会话连上来,按顺序分到组里不同的临时表空间。**
**这样十个会话同时做大排序,会分散到三个文件上,而不是挤一个。**
**上表里 180/40/55 的分布就是「分配不均」的样子**——
**轮到哪个是哪个,不看谁空。所以组不是负载均衡,是「分散」。**
**建库时如果没建临时表空间组,默认只有一个 `TEMP`。**
**单实例跑跑没问题;一旦并发排序多,`ORA-01652` 就会开始出现。**
**治法是加文件或者建组,别只盯着「把 `TEMP` 调大」**—— **单个文件太大,争用反而更集中。**
临时表空间的两个禁忌
--------------------------------------------------------------------
**① 不能在里面建永久对象。** **`CREATE TABLE ... TABLESPACE temp` 直接报错**——
**因为临时表空间的内容不写 redo、不保证持久,放永久数据等于自毁。**
**② 不能把 `TEMP` 删掉。** **它是数据库的默认临时表空间,删了所有排序都没地方落。**
**要换就新建一个、切成默认、再删旧的**:
**`ALTER DATABASE DEFAULT TEMPORARY TABLESPACE temp2;`**
**顺带一个排查技巧:临时表空间「用完了但不释放」是正常现象。**
**临时段用完会标记为空闲,但文件不缩小**——
**所以看「临时表空间用了 90%」不一定是问题,要看 `v$tempseg_usage` 里是谁在用。**
第一段:一万行分 5 段,每段 2000 行、约 400KB,总落盘约 2MB。
这就是外部排序的「分块排、写盘、归并」三步里的前两步。
注意「约 2MB」这个数很小——一条语句的临时空间通常不是问题,问题是并发。
所以下面三行才是重点:10 个会话要 19MB,100 个会话要 191MB。
并发放大是线性的,但现实里会更糟——
因为会话不是同时开始同时结束,峰值往往出现在「大家都排到一半」的时候。
这就是为什么临时表空间要按「峰值并发 × 单语句量 × 2」给。
最后六行是临时表空间组的分配:会话 1 到 3 分别落到 temp01/temp02/temp03,
第 4 个会话又回到 temp01。
这就是「轮流」,不看谁空。
所以会出现「一个用了 180MB、另一个只用 40MB」的情况——这是分配策略决定的,不是 bug。
要真均衡,得靠监控 + 手工调,组本身做不到。
30 秒实验
- 把
mem_rows从 2000 改成 5000,重跑。段数从 5 降到 2,落盘量不变。 这说明「内存大一点,段少一点,但总量差不多」—— 内存只减少「段数」,减少不了「要排的数据量」。 - 把
rows从 10000 改成 100000,重跑。落盘量涨十倍。 这就是「大表排序要盯临时表空间」的原因。 - 把
members改成两个,重跑。六个会话分成 2/2/2。 组里成员越多越分散,但每个成员的争用也越少——代价是管理复杂度上升。
常见误区
- 用
V$DATAFILE查临时文件。 查不到。临时文件在V$TEMPFILE/DBA_TEMP_FILES里。 - 看到临时表空间用了 90% 就急着扩容。
临时段用完不释放是正常的,要看
V$TEMPSEG_USAGE里有没有活跃会话。 - 把永久表建在临时表空间里。 直接报错。它不写 redo、不保证持久,放永久数据等于自毁。
- 以为临时表空间组是负载均衡。 它是「轮流分配」,不看谁空,会出现明显不均。
自测三问(点开对答案)
1. 临时表空间里的东西为什么不备份? —— **因为它存的是「算到一半的中间结果」**,不写 redo、不走 undo。 **崩溃后直接丢弃,重开库重新初始化。** **备份一个中间结果没有意义。** 2. `ORA-01652` 是什么问题? —— **临时表空间不够,扩不出临时段。** **表现是「一条查询跑着跑着失败」**,不是写数据失败。 3. 临时表空间组解决什么问题? —— **并发排序挤在一个文件上的争用。** **它把会话轮流分散到多个临时表空间**,**注意是「分散」不是「均衡」。** 4. 临时表空间「用了 90% 但不释放」正常吗? —— **通常正常。** 临时段用完标记空闲但文件不缩。**要看 `V$TEMPSEG_USAGE` 里有没有活跃会话。**第 8 章到此为止。表空间、数据文件、undo、temp——存储这一层算是走完了。 下一篇进第 9 章:数据表对象。 话题从「空间怎么分」转到「表怎么设计」。
- ☐ (本篇完,下一篇 9.1 数据表概述)
相关笔记
- 管理撤销表空间
- 表空间与数据文件的关系
- 创建表空间
-
- 🐬 MySQL必知必会/00-索引
- 🏺 Oracle从入门到精通 · 索引