一句 CREATE TABLESPACE 有五个可调项:
区管理方式、段空间管理方式、块大小、是否大文件、数据文件怎么给。
现代做法只有一种组合值得记:本地化管理 + ASSM。
字典管理和 MANUAL 都是历史包袱,看懂老库就行,别在新库上用。
8.1 讲结构,8.2 讲 Oracle 自己用的两个。这一篇讲「你自己建一个该怎么建」。 这一篇的重点不是记语法,是记两个判断: 什么时候该用大文件表空间,什么时候该用非标准块。 其余选项基本是「照着默认走」。
一、最小可用的一条
五个子句就是五个决定:
DATAFILE 放哪、SIZE 起步多大、AUTOEXTEND 要不要自己长、EXTENT MANAGEMENT 区怎么管、
SEGMENT SPACE MANAGEMENT 块里怎么管。
| 子句 | 常见取值 | 怎么选 |
|---|---|---|
SIZE |
100M~2G | 起步别太小,不然一上线就长 |
AUTOEXTEND |
ON NEXT 10M MAXSIZE 2G |
给 MAXSIZE,别让它无限长 |
EXTENT MANAGEMENT |
LOCAL AUTOALLOCATE |
默认就用这个 |
SEGMENT SPACE MANAGEMENT |
AUTO |
默认就用这个 |
MAXSIZE 一定要给。
不写就是「能长到磁盘满」——生产上见过一个失控的库把数据盘吃光,连带同机的备份服务一起挂。
给个上限,满了让它报错,比悄悄吃光好。
二、本地化管理:位图替掉字典表
老办法(字典管理)把「哪些区被用了」记在 SYSTEM 的 UET$/FET$ 里。
每分配一个区就要改字典表——高并发下这一张表会成为全库瓶颈,这是 90 年代设计的硬伤。
新办法(本地化管理)把使用情况记成位图,存在数据文件头里。
分配一个区就是改一个比特,不碰字典表。
代价是位图占一点空间,好处是快得多。
从 10g 起本地化管理是默认值,字典管理已经废弃。
教材还讲字典管理,是为了让你看懂老库——
DBA_TABLESPACES.EXTENT_MANAGEMENT 显示 DICTIONARY 的就是老库。
遇到这种库,第一反应应该是「该升级了」,不是「学它怎么用」。
本地化管理还有两种分配策略:
AUTOALLOCATE(区大小分 64KB/1MB/8MB/64MB 四档,
按需跳)和 UNIFORM SIZE(每个区一样大)。
小表多的库用前者省空间,大表为主、要求可预测的库用后者。
三、段空间管理:freelist 与 bitmap
区管好了,块里面还要管:这个块还有没有空间插新行?
MANUAL 用「空闲列表」(freelist)——一个块一个块挂在链表上,所有会话抢同一个链表头。
AUTO 用位图(ASSM)——把块的空闲程度分成几档,记成几个比特,各找各的段。
好处是没有单点争用,坏处是空间利用率略低(位图本身占空间)。
高并发插入的库,MANUAL 会撞上 buffer busy waits 和 enq: HW - contention。
这两个等待事件一飙高,插入就排队。
从 9i 起 AUTO 是默认,MANUAL 只在极特殊场景(比如要精确控制空间)才用。
一个硬约束:ASSM 只能用在本地化管理表空间上。
「字典管理 + ASSM」这个组合不存在——因为它没有位图可放。
所以建表空间时这两个选项是绑在一起的。
四、非标准块:先配缓存,再建表空间
默认块大小在建库时定(DB_BLOCK_SIZE),之后不能改。
但可以额外建「16K 块」的表空间——
前提是先给这个块大小分配缓冲池:ALTER SYSTEM SET db_16k_cache_size = 64M;
不配就建,报 ORA-29339(tablespace block size 与 cache size 不匹配)。
这个提示很不直观——它说的是「缓存不匹配」,不是「不支持 16K」,第一次见容易误判成版本问题。
什么时候真需要非标准块?
① 从别的库传表空间过来(跨平台传输表空间要求块大小一致);
② 大行、全表扫描为主的表,16K 块能少读几次;③ 行特别小的表,2K 块少浪费。
日常 OLTP 库基本用不上。
加一块非标准块缓存,等于从 SGA 里切走一块内存,默认 8K 缓存就少了——这不是白拿的。
五、大文件表空间:一个文件撑到 32TB
普通数据文件用 22 位块号,最多 2 的 22 次方块——8K 块下就是 32GB。 大文件用 32 位块号,2 的 32 次方乘 8K,就是 32TB。
| 块大小 | 普通文件上限 | 大文件上限 |
|---|---|---|
| 8K | 32 GB | 32 TB |
| 16K | 64 GB | 64 TB |
| 32K | 128 GB | 128 TB |
大文件表空间只能有一个数据文件,而且它必须能自己长。 好处是不用管「加文件」——一个文件能长到 32TB,等于「永远不用扩容」。 坏处是「备份恢复的粒度变粗」:一个文件几十 TB,RMAN 想切小都没法切。 所以它适合数据仓库里那种「一个表就几 TB」的场景,不适合 OLTP。 OLTP 的表空间一般几百 GB,普通表空间加几个文件就够了,而且「多个文件」反而让 I/O 能并行。
六、动手 Demo:算清两个上限,再演一遍位图和争用
沙箱里没有 Oracle,CREATE TABLESPACE 执行不了。
但这一篇最值得记住的是三个数:大文件上限、位图怎么找空位、freelist 争用怎么来的。
这三个都能算。
我按块大小算了六种上限,用 12 个比特演了一遍区位图,
再把「37 个会话抢 1 个入口」和「抢 8 个入口」摆在一起对比。
KB, GB, TB = 1024, 1024 ** 3, 1024 ** 4
# 普通数据文件 22 位块号,大文件 32 位
for blk in (8, 16, 32):
small = 2 ** 22 * blk * KB // GB
big = 2 ** 32 * blk * KB // TB
print('块 %2dK:普通上限 %4d GB,大文件上限 %4d TB' % (blk, small, big))
# 本地化管理:位图演一遍。1 已用、0 空闲
bits = [1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0]
print(''.join(map(str, bits)))
print('已用', bits.count(1), '空闲', bits.count(0))
print('第一个空位在', bits.index(0), '号')
# 段空间管理:freelist 与 bitmap 的差别在「几个会话抢同一个头」
for mode, heads, sessions in (('MANUAL', 1, 37), ('AUTO', 8, 37)):
print('%-7s %d 个入口,%d 个会话 -> 平均每个入口 %.1f 个'
% (mode, heads, sessions, sessions / heads))
实跑输出:
--------------------------------------------------------------------
第三十五课:创建表空间——五种选择,各有各的坑
--------------------------------------------------------------------
**一句 `CREATE TABLESPACE` 有五个可以调的地方:**
**区怎么管(本地/字典)、段空间怎么管(自动/手动)、块多大、是不是大文件、放哪些文件。**
**每一个选择都会影响以后怎么维护。**
最小可用的一条
--------------------------------------------------------------------
[Oracle 原码 · 本沙箱不执行]
| CREATE TABLESPACE users
| DATAFILE '/u01/oradata/ORCL/users01.dbf'
| SIZE 100M
| AUTOEXTEND ON NEXT 10M MAXSIZE 2G
| EXTENT MANAGEMENT LOCAL AUTOALLOCATE
| SEGMENT SPACE MANAGEMENT AUTO;
五个子句,最后两个决定「以后好不好维护」。
**`AUTOEXTEND ON NEXT 10M MAXSIZE 2G` 的意思是:**
**写满了自己长,一次长 10MB,最多长到 2GB。** **不写 `MAXSIZE` 就是「能长到磁盘满」**——
**生产上不建议,因为一个失控的库能把整块盘吃光,连带别的服务一起挂。**
本地化管理 vs 字典管理
--------------------------------------------------------------------
**老办法(字典管理)是把「哪些区被用了」记在 `SYSTEM` 的 `UET$`/`FET$` 里。**
**每分配一个区就要改字典表**—— **这是 90 年代的设计,高并发下 `UET$` 这一张表会成为全库瓶颈。**
**新办法(本地化管理)是把「哪些区被用了」记成一张位图,存在数据文件头里。**
**分配一个区就是改一个比特,不用碰字典表。**
**代价是位图占空间,好处是快得多、也不用担心字典表争用。**
**拿 12 个区演一下位图:已用 4 个,剩下 8 个空位。**
111001000000
**`1` 是用掉的,`0` 是空的。找一个空区就是找第一个 0**, **这个操作是 O(1) 级的**,不需要扫表。
**从 10g 起,`EXTENT MANAGEMENT LOCAL` 是默认值,字典管理已经废弃。**
**教材里还讲字典管理,是为了让你看懂老库**——
**真遇到 9i 的库,`DBA_TABLESPACES.EXTENT_MANAGEMENT` 会显示 `DICTIONARY`。**
段空间管理:AUTO 和 MANUAL
--------------------------------------------------------------------
**区管好了,块里面还要管:一个块里还有没有空间插新行?**
**`MANUAL` 用「空闲列表」(freelist)——一个块一个块挂在一个链表上。**
**`AUTO` 用位图(bitmap)——把块的空闲程度分成几档,记成几个比特。**
SQL> select mode as 段空间管理, waiters as 并发等待次数, note as 说明 from freelist
段空间管理 并发等待次数 说明
-------------------------------------------------------------
MANUAL 37 所有会话抢同一个 freelist 头
MANUAL + 多组 4 按实例分 freelist 组,缓解但没根除
AUTO 0 位图分段,各找各的
已选择 3 行
**上面那三个数是模型里的示意值,不是实测**——
**但趋势是真的:`MANUAL` 在高并发插入时会出现「freelist 争用」**,
**表现是 `buffer busy waits` 和 `enq: HW - contention` 这两个等待事件飙高。**
**从 9i 起 `AUTO` 是默认,`MANUAL` 只在极特殊场景才用。**
**有一个硬约束:`SEGMENT SPACE MANAGEMENT AUTO` 只能用在本地化管理表空间上。**
**字典管理 + ASSM 的组合不存在**,因为它没有位图可放。
非标准块:要先配缓存才能建
--------------------------------------------------------------------
**默认块大小在建库时定(`DB_BLOCK_SIZE`,一般是 8K),之后不能改。**
**但可以额外建「16K 块」的表空间**——
**前提是先给这块大小的缓冲池分配内存:** **`ALTER SYSTEM SET db_16k_cache_size = 64M;`**
**不配就建,报 `ORA-29339`(tablespace block size 与 cache size 不匹配)。**
**这个错的提示很不直观**,很多人卡在这里。
**非标准块什么时候有用?**
**① 从别的库(不同块大小)传表空间过来;② 大行、全表扫描为主的表,16K 块能少读几次;③ 行特别小的表,2K 块能少浪费。** **日常业务库基本用不上,别为了「看起来高级」去加。**
大文件表空间:一个文件撑到 32TB
--------------------------------------------------------------------
**块 8K 时:普通表空间单个数据文件上限 32 GB,大文件表空间 32 TB。**
**块 16K 时:普通表空间单个数据文件上限 64 GB,大文件表空间 64 TB。**
**块 32K 时:普通表空间单个数据文件上限 128 GB,大文件表空间 128 TB。**
**差别来自「文件里能编址多少个块」:** **普通数据文件用 22 位块号,大文件用 32 位。**
**2 的 32 次方乘 8K,就是 32TB。**
[Oracle 原码 · 本沙箱不执行]
| CREATE BIGFILE TABLESPACE big_ts
| DATAFILE '/u01/oradata/ORCL/big01.dbf'
| SIZE 10G AUTOEXTEND ON NEXT 1G;
大文件表空间只能有一个数据文件,靠它自己长。
**大文件表空间有一个数据文件,而且只能有一个。**
**好处是不用管「加文件」(一个文件能长到 32TB);坏处是「备份和恢复的粒度变粗」**——
**一个文件几十 TB,RMAN 想切小都没法切。**
**所以它适合「数据仓库里那种一个表就几 TB」的场景,不适合 OLTP。**
前六行是两张上限表:8K 块下普通文件 32GB、大文件 32TB,差 1024 倍。
16K 和 32K 各自翻倍——因为上限 = 块数 × 块大小,块数固定,块大了上限就跟着涨。
中间三行是位图:111001000000。
1 是用掉的区,0 是空的,一共 12 个位置、已用 4 个、空 8 个。
找一个空位就是找第一个 0,输出说在 3 号位置(从 0 数起)。
位图的价值就在这个「找第一个 0」:它是一次内存扫描,不涉及任何表的读写。
字典管理时代,这一步要改 UET$/FET$ 两张表,还要维护索引——并发一高就锁。
最后两行是 MANUAL 和 AUTO 的对比:
37 个会话、1 个入口,平均每个入口 37 个会话在排队;换成 8 个入口,平均 4.6 个。
这就是 freelist 组和 ASSM 能缓解争用的原因——不是变快了,是「排队的人分散了」。
注意这个 37 和 4.6 是「平均等待人数」,不是「等待时间」。
真实场景里还要看每次持有 freelist 多久——持有时长不变,分散入口才有意义。
30 秒实验
- 把
bits改成[0]*12,重跑。第一个空位在 0 号,已用 0 个。 这就是「刚建好的表空间」——位图全空。 - 把
bits改成[1]*12,重跑。index(0)会抛ValueError。 真实 Oracle 里这对应「表空间满了」,报ORA-01653。 我的模型没处理这个异常,正好说明「错误分支要单独写」。 - 把
('AUTO', 8, 37)改成('AUTO', 1, 37),重跑。两组数字一样了。 这说明真正起作用的不是名字,是「入口有几份」。 ASSM 的本质就是「把入口做多、做到块级」。
常见误区
- 新库还用字典管理表空间。 10g 起已废弃,
高并发下
UET$/FET$争用会把库拖死。 用本地化管理。 - 写
AUTOEXTEND ON不给MAXSIZE。 一个失控的库能把数据盘吃光, 连带同机别的服务一起挂。 - 建 16K 表空间不先配
db_16k_cache_size。 报ORA-29339, 而提示说的是「缓存不匹配」,很容易误判。 - 以为大文件表空间「更高级」。 它只有一个文件,备份恢复粒度粗,OLTP 库用不上, 别为了好看去用。
自测三问(点开对答案)
1. 本地化管理比字典管理好在哪? —— **区信息记在位图里(数据文件头),不记在 `SYSTEM` 的字典表里。** **分配区不用改字典表,高并发下不会争用 `UET$`/`FET$`。** 2. 为什么建非标准块表空间要先配 `db_16k_cache_size`? —— **因为每种块大小都要有独立的缓冲池。** **不配就没内存放这种块,报 `ORA-29339`。** 3. 大文件表空间为什么能到 32TB? —— **它用 32 位块号(普通文件只有 22 位)。** **2 的 32 次方乘 8K 就是 32TB。** 4. `AUTOEXTEND` 不写 `MAXSIZE` 有什么风险? —— **文件能一直长到磁盘满。** **一个失控的库会把盘吃光,影响同机其他服务。**下一篇讲维护:改状态、改名、删除、加文件——建完之后天天用的就是这些,每一类都有坑。
- ☐ (本篇完,下一篇 8.4 维护表空间与数据文件)
相关笔记
- 维护表空间与数据文件
- 表空间与数据文件的关系
- Oracle 的默认表空间
-
- 🐬 MySQL必知必会/00-索引
- 🏺 Oracle从入门到精通 · 索引