🏺 第8章 管理表空间和数据文件
📄 一句话概括

四类操作天天用:改状态(read only/offline)、改默认、改名、删除扩容。 两个最容易踩的坑:READ ONLY 之后写报的是 ORA-00372(听着像磁盘问题); DROP ... INCLUDING CONTENTS 删完磁盘不变——文件还在。

📝 本章和前面几章的关系

8.3 讲怎么建。这一篇讲建完之后怎么管。 日常 DBA 花在维护上的时间,比在建表空间上多得多。 这一篇有两条和后面连着: READ ONLY 是报表库、历史库的常用手段; OFFLINE 是「单独维护一个表空间」的前提,第 16 章做部分恢复时要用。

一、四种状态:能读能写不一样

状态 能读 能写 什么时候用
ONLINE 能 能 正常
READ ONLY 能 不能 报表库、历史数据
OFFLINE 不能 不能 维护时摘出去
OFFLINE IMMEDIATE 不能 不能 急着摘,不等检查点

READ ONLY 之后写会报 ORA-00372(file cannot be modified at this time)。 这个错很迷惑——它说的是「文件现在不能改」,不是「表空间只读」, 第一次见的人常以为是磁盘权限或者文件系统只读。 OFFLINE 之后读会报 ORA-00376(file cannot be read at this time)。 这两个码的区别值得记:一个说「不能改」,一个说「不能读」,对应的状态一个是 READ ONLY, 一个是 OFFLINE。 SYSTEM 表空间不能 OFFLINE、不能 READ ONLY。 因为它随时要被读(字典)。 UNDO 表空间也不能随便 OFFLINE——正在跑的事务需要它。

二、设置默认表空间:两级,还要配配额

两级默认:数据库级和用户级。 用户没单独指定时,用数据库级的那个。 ALTER DATABASE DEFAULT TABLESPACE app_data; ALTER USER scott DEFAULT TABLESPACE app_data; 改了默认不等于给了配额。 用户在新表空间上没有配额,建表照样失败(ORA-01950)。 所以要成对做:ALTER USER scott QUOTA UNLIMITED ON app_data; SYSTEM 不能当默认表空间。 想设成 SYSTEM 会被拦住——Oracle 干脆不让你把用户数据往字典里塞。 这个保护挺难得,很多数据库都没这种限制。

三、重命名:改表空间一句话,改文件路径要四步

改表空间名:ALTER TABLESPACE users RENAME TO app_data; 只改字典里的名字,物理文件一个字节都不动,几毫秒完事。 但要记得:脚本、监控、备份策略里写死的名字都得跟着改。 改数据文件的路径就麻烦多了,四步: ① ALTER TABLESPACE ts OFFLINE;(或把库起到 mount) ② 操作系统层 mv 文件 ③ ALTER TABLESPACE ts RENAME DATAFILE '旧' TO '新'; ④ ALTER TABLESPACE ts ONLINE; 顺序不能乱。 先 mv 后 offline 的话,offline 那一刻它要写文件头,找不到文件就报错。 这一点和 7.2 节改日志文件位置是同一个套路—— 都是「先让对象停用,再动文件,再登记,再启用」。

四、删除:三档写法,破坏力递增

写法 删什么 磁盘会空出来吗
DROP TABLESPACE ts 只删空表空间 —(有东西就报错)
... INCLUDING CONTENTS 连段一起删 不会,文件还在
... INCLUDING CONTENTS AND DATAFILES 连文件一起删 会

最容易踩的是中间那档:删完发现磁盘没少。 因为 INCLUDING CONTENTS 只清「库里的登记」,物理文件还躺在那儿。 得手工去操作系统删。 这和 7.2 节删日志组是同一个毛病。 还有个变体:... AND DATAFILES 在某些平台上也只是「标记可删」, 文件系统层面还会留一段时间。 所以删完记得去 ls 一眼,别只看视图。 SYSTEM 删不掉(ORA-01550)。 UNDO 和 TEMP 要用专门的语句,而且要先有替代品—— 比如删 UNDO 之前得先切到另一个 undo 表空间。

五、维护数据文件:加、改、自动增长

三种扩法,各有适用场景:

做法 适合 注意
RESIZE 500M 文件没到系统上限 只能改大不能改小(缩小要重建)
ADD DATAFILE 单文件到顶了 引入「区不能跨文件」的碎片
AUTOEXTEND ON 想省心 一定给 MAXSIZE

RESIZE 往小改一般会失败,因为文件尾部可能还存着数据。 要缩小得先 ALTER TABLE ... MOVE 把数据挪走,或者用 SHRINK。 ADD DATAFILE 是扩容最常用的手段,但它有副作用: 文件一多,「区不能跨文件」造成的碎片就多。 所以别一个 100MB 一个 100MB 地加,一次给足。 AUTOEXTEND 的 NEXT 也别设太小。 NEXT 10M 意味着写满一次就要等一次扩展——扩展是有开销的(要改文件头、更新控制文件)。 NEXT 100M 或 NEXT 1G 更省事,代价是可能多占一点空间。

六、动手 Demo:算一遍扩容账,看清三档删除的差别

沙箱里没有 Oracle,ALTER TABLESPACE 执行不了。 但「100MB 能装多少行」「长到 2GB 要长几回」这些账,是可以算准的。 我算了 100MB 的文件能装多少行,再算自动增长要长多少回, 最后把三档删除语句并排摆出来,指出哪一档磁盘不会空。

MB, ROWLEN = 1024 * 1024, 100
usable = 8192 - 100
per_blk = usable // ROWLEN

cur_mb, next_mb, max_mb = 100, 10, 2048
blocks = cur_mb * MB // 8192
print('%dMB = %d 个 8K 块,约 %d 行' % (cur_mb, blocks, blocks * per_blk))

# 自动增长的次数:从现在长到 2GB 要长多少回
grow = 0
size = cur_mb
while size < max_mb:
    size = min(size + next_mb, max_mb)
    grow += 1
print('每次长 %dMB,长到 %dMB 要长 %d 回' % (next_mb, max_mb, grow))

# 三档删除,破坏力递增
for sql in ('DROP TABLESPACE app_data',
            'DROP TABLESPACE app_data INCLUDING CONTENTS',
            'DROP TABLESPACE app_data INCLUDING CONTENTS AND DATAFILES'):
    print(sql)

print('第二种删完磁盘不变——文件还在,得自己去操作系统删')

实跑输出:

--------------------------------------------------------------------
第三十六课:维护表空间——改状态、改名、删除、加文件
--------------------------------------------------------------------

  **建完之后真正天天用的,是这一节的东西。**  **状态、改名、删除、扩容,四类操作,每一类都有坑。**

  四种状态,能干什么不一样
  --------------------------------------------------------------------

  SQL> select state as 状态, read as 读, write as 写, note as 说明 from st
  状态              读     写     说明
  ----------------------------------------------------
  ONLINE            能读   能写   正常状态
  READ ONLY         能读   不能写 报表库、历史数据常用
  OFFLINE           不能读 不能写 维护时临时摘出去
  OFFLINE IMMEDIATE 不能读 不能写 不等检查点,立刻摘
  已选择 4 行
  **`READ ONLY` 之后写会报 `ORA-00372`(file cannot be modified at this time)。**
  **注意这个错的表现很迷惑**:
  **它说的是「文件现在不能改」,不是「表空间只读」**——  **第一次见的人常以为是磁盘权限问题。**
  **`OFFLINE` 之后读会报 `ORA-00376`(file cannot be read at this time)。**
  **`OFFLINE` 和 `OFFLINE IMMEDIATE` 的差别是「等不等检查点」:**
  **前者会先把脏块刷完再摘,可能要等;后者立刻摘,脏块留在那儿。**
  **急着停机维护用 `IMMEDIATE`,正常维护用普通的。**
  **`SYSTEM` 表空间不能 `OFFLINE`、不能 `READ ONLY`。**
  **因为它随时要被读(字典),摘了等于库废了。**

  设置默认表空间:两级
  --------------------------------------------------------------------
  **两级默认:数据库级的默认表空间,和用户级的默认表空间。**
  **用户没单独指定时,就用数据库级的那个。**

  [Oracle 原码 · 本沙箱不执行]
  | ALTER DATABASE DEFAULT TABLESPACE users;
  | ALTER USER scott DEFAULT TABLESPACE users;

  **一个常见的坑:改了默认表空间,不代表「配额」跟着给。**
  **用户在新表空间上没有配额,建表照样失败**(`ORA-01950`:no privileges on tablespace)。
  **所以要成对做:`ALTER USER scott QUOTA UNLIMITED ON users;`**
  **另一个坑:`SYSTEM` 不能当默认表空间。**  **想设成 `SYSTEM` 会报 `ORA-12914` 一类**——
  **Oracle 干脆不让你把用户数据往字典里塞。**

  重命名表空间:一句话
  --------------------------------------------------------------------

  [Oracle 原码 · 本沙箱不执行]
  | ALTER TABLESPACE users RENAME TO app_data;
    10g 起支持,只改名字不挪数据。

  **改的是字典里的名字,物理文件一个字节都不动。**  **所以这句很快,几毫秒就完事。**
  **但要记得:所有脚本、监控、备份策略里写死的表空间名,都得跟着改。**
  **改数据文件的路径就不一样了,那要动文件**,
  **得先 `offline`(或把库起到 mount),再 `ALTER TABLESPACE ... RENAME DATAFILE ... TO ...`。**
  **顺序不能乱**:先 `offline` → 操作系统 `mv` → `RENAME DATAFILE` 登记 → `online`。
  **先 `mv` 后 `offline` 的话,`offline` 那一刻它要写文件头,找不到就报错。**

  删除表空间:带不带那两个词差很多
  --------------------------------------------------------------------

  [Oracle 原码 · 本沙箱不执行]
  | DROP TABLESPACE app_data INCLUDING CONTENTS AND DATAFILES;
    不带 INCLUDING CONTENTS 就直接报错,除非表空间是空的。

  **三档写法,破坏力递增:**
  **`DROP TABLESPACE ts;`——只删空表空间,里面有东西就报 `ORA-01549`。**
  **`... INCLUDING CONTENTS`——连里面的段一起删,但磁盘文件还在。**
  **`... INCLUDING CONTENTS AND DATAFILES`——连文件也删,磁盘真的空出来。**
  **最容易踩的是中间那档:删完发现磁盘没少。**
  **因为 `INCLUDING CONTENTS` 只清「库里的登记」,物理文件还躺在那儿**,得手工去操作系统删。
  **这和 7.2 节删日志组是同一个毛病。**
  **`SYSTEM` 表空间删不掉,报 `ORA-01550`。**  **`UNDO` 和 `TEMP` 要用专门的语句删**
  (`DROP TABLESPACE ... INCLUDING CONTENTS AND DATAFILES` 对 temp 不行,
  temp 要走 `DROP TABLESPACE ts INCLUDING DATAFILES` 且先切走默认)。

  扩容:加文件、改大小、开自动增长
  --------------------------------------------------------------------
  **假设表空间现在 100MB,写满了。三种扩法:**
  **① `ALTER DATABASE DATAFILE '...' RESIZE 500M;`**——
  **直接把现有文件改大,最省事,但受文件系统单文件上限约束。**
  **② `ALTER TABLESPACE ts ADD DATAFILE '...' SIZE 100M;`**——
  **再加一个文件,能绕过单文件上限,但会引入「区不能跨文件」的碎片问题。**
  **③ `ALTER DATABASE DATAFILE '...' AUTOEXTEND ON NEXT 10M MAXSIZE 2G;`**——
  **让它自己长,省心,但要盯 `MAXSIZE`。**
  **算一下 100MB 能装多少行:每块 80 行,100MB 是 12800 个块,约 13107 行。**
  **所以一张一千万行的表,光数据就要 954MB 左右**——
  **这还没算索引。**
  **估算容量时按「行数 × 行长 × 1.3」粗算(含 `PCTFREE` 和块头开销),比拍脑袋强。**

第一行:100MB 等于 12800 个 8K 块,按每块 80 行算,约 102 万行。 记住「100MB 大约装一百万行(行长 100 字节)」这个数量级,估容量的时候很好用。 第二行:每次长 10MB,从 100MB 长到 2048MB 要长 195 回。 每回都要改文件头、更新控制文件——这就是 NEXT 别设太小的原因。 195 这个数还说明另一件事:MAXSIZE 2G 一旦长满,就再也不长了,接下来就是 ORA-01653。 所以 MAXSIZE 要给足,别按「现在够用」来设。 下面三行是三档删除语句。 第二档(INCLUDING CONTENTS)删完磁盘不变—— 这是新手最常踩的坑:以为删干净了,结果 df 一看一点没少。 第三档(AND DATAFILES)才会真删文件。 但即使在第三档,也建议删完去 ls 一眼——有些平台上文件句柄要等进程退出才真正释放。

30 秒实验

  1. 把 ROWLEN 从 100 改成 200,重跑。行数从 102 万降到 51 万。 行长翻倍,能装的行减半——这是估算容量最基本的直觉。
  2. 把 next_mb 从 10 改成 100,重跑。增长次数从 195 降到 20。 少了九成——这就是「NEXT 设大一点」的量化依据。
  3. 把 max_mb 从 2048 改成 200,重跑。增长次数变成 10 次。 MAXSIZE 越小,越早撞上 ORA-01653。

常见误区

  1. DROP TABLESPACE ... INCLUDING CONTENTS 之后以为磁盘空了。 不会,文件还在。 要加 AND DATAFILES,或者手工删。
  2. READ ONLY 之后看到 ORA-00372 以为是磁盘权限问题。 是表空间只读,不是磁盘只读。
  3. 改数据文件路径时先 mv 再 offline。 offline 要写文件头,找不到文件就报错。 顺序是「先停用,再动文件」。
  4. AUTOEXTEND ON NEXT 10M 不给 MAXSIZE。 能长到磁盘满,把同机别的服务一起拖死。
自测三问(点开对答案) 1. `READ ONLY` 和 `OFFLINE` 报的错有什么不同? —— **`READ ONLY` 写会报 `ORA-00372`(file cannot be modified)。** **`OFFLINE` 读会报 `ORA-00376`(file cannot be read)。** 2. 为什么 `DROP ... INCLUDING CONTENTS` 删完磁盘没变? —— **它只清库里的登记,不删物理文件。** **要加 `AND DATAFILES`,或者自己去操作系统删。** 3. 改数据文件路径的四步是什么? —— **`OFFLINE` → 操作系统 `mv` → `RENAME DATAFILE` → `ONLINE`。** **顺序不能乱,先 `mv` 会让 `OFFLINE` 失败。** 4. `ADD DATAFILE` 有什么副作用? —— **文件一多,「区不能跨文件」造成的碎片就多。** **所以别零碎地加,一次给足。**

下一篇讲撤销表空间:回滚、读一致性、闪回查询,三件事都靠它。 ORA-01555 这个经典错误也会在这一篇算清楚。

相关笔记