🏺 第16章 Oracle 数据备份与恢复
📄 一句话概括

RMAN 不是「一条备份命令」,是一套客户端加服务端的框架。 四个部件:目标库、恢复目录、通道、备份片。 它比「复制文件」多出来的东西是三样:增量、校验、元数据。 元数据是关键——它记得「哪个备份片里有什么」,所以恢复时不用你翻文件名。

📝 本章和前面几章的关系

16.1 讲的是原理:物理备份、热备、归档。这一篇讲 Oracle 里干这件事的工具。 后面三篇(16.3 备份、16.4 完全恢复、16.5 部分恢复)用的全是这一篇的概念: 通道开几个、备份片怎么分、元数据从哪儿查。

一、RMAN 比「复制文件」多出什么

最朴素的热备就是「复制文件」,DBA 手工也能做。RMAN 把这件事接管过来,多了四样东西:

能力 手工复制 RMAN
增量备份 做不到 按块比对,只备变化的部分
备份校验 要另外算校验和 备份时就算块校验值
元数据 靠自己记文件名 记在控制文件或恢复目录里
自动找备份 做不到 恢复时说「要哪天的」,它自己找

第三行是 RMAN 真正的价值所在。 手工备份最大的风险不是「备份失败」,是「恢复的时候找不到该用哪个文件」。 RMAN 把「哪次备份、包含哪些文件、对应哪个 SCN」全记下来了, 恢复时你只要说「恢复到昨天中午」,它自己算该用哪几个备份片。 第四行也一样:手工恢复要你自己算「备份 + 哪几段归档」,RMAN 自己从元数据里推。 这就是为什么 DBA 宁可学一套命令,也不愿意写复制脚本。

二、四个部件:目标库、恢复目录、通道、备份片

① 目标库(target)——被备份的那个库。RMAN 连上去之后,所有操作都针对它。 ② 恢复目录(catalog)——单独一个数据库,专门存备份元数据。 它是可选的:不建,元数据就存在目标库的控制文件里。 ③ 通道(channel)——一条执行备份的「工作通道」。 可以开多个并行干活,也可以指定是磁盘还是磁带。 ④ 备份片(backup piece)——一次备份产出的文件。一个通道产生一个备份片,多个通道就产生多个。 还有个第五样东西容易被漏掉:快照控制文件(snapshot controlfile)。 备份的时候控制文件一直在变,RMAN 先做一个快照,然后读快照——这样读到的是同一时刻的一致视图。 它不需要你管,但看到 $ORACLE_HOME/dbs/snapcf_*.f 这种文件别删。 恢复目录到底建不建,判断标准只有一条:控制文件丢了你能不能接受。 不建:控制文件一丢,元数据全丢,你只知道「有一堆备份文件」,但不知道哪个是哪天的。 这时候只能靠 CATALOG 命令重新扫描登记,很麻烦。 建了:元数据在另一个库里,目标库整个没了都还在。

三、通道:并行度与设备类型

通道的语法是 ALLOCATE CHANNEL c1 DEVICE TYPE DISK。 DEVICE TYPE 两种:DISK 是本地磁盘或挂载的存储,SBT 是磁带库(要靠厂商的介质管理库)。 平时不用手工 ALLOCATE——配了默认设备类型之后, BACKUP DATABASE 会自动按 PARALLELISM 开通道。 要控制并行度,改 CONFIGURE DEVICE TYPE DISK PARALLELISM 4。 并行度开多少?经验是「不超过 CPU 核数,也不超过物理磁盘数」。 这句话的后半句比前半句重要:一块盘开四个通道,四个通道抢同一份带宽,收益远不到四倍。 这一篇的 Demo 会把这个数真的量出来。 还有一个常被忽略的点:通道的 MAXPIECESIZE 和 FILESPERSET。 前者限制单个备份片多大(比如备份到 FAT32 上就得设),后者限制一个备份集里放几个文件。 这两个参数只在「备份文件太大不好管」的时候才需要动——平时让 RMAN 自己分就行。

四、RMAN 命令的三种写法

① 交互式:rman target / 进去,一条条敲。适合临时排查。 ② 命令行单条:把命令写进 cmdfile 参数指定的脚本。适合放进调度系统。 ③ 脚本块(RUN { }):把一串命令包起来,里面可以 ALLOCATE、BACKUP、RELEASE。 适合固定流程。 最常用的其实是第二种加第三种混着来:把 RUN 块写进 .rman 文件,由操作系统的定时任务调用。 注意 .rman 文件里不要写 SQL*Plus 的 / 结尾——那是 SQL*Plus 的规矩,RMAN 不认。 常用命令记五个就够起步: BACKUP 备份、RESTORE 把文件铺回去、RECOVER 应用日志、LIST BACKUP 看有什么、 REPORT 看该做什么。 RESTORE 和 RECOVER 的区别要记牢:前者是「把文件复制回来」,后者是「把日志应用上去」。 完全恢复两步都要,NOARCHIVELOG 只能做第一步。

五、元数据存在哪儿,决定了灾难时能查到什么

元数据(哪个备份、哪个 SCN、哪个文件)有两个存放地:控制文件、恢复目录。 RMAN 查询时先看恢复目录,没有再看控制文件。

场景 只有控制文件 有恢复目录
控制文件损坏 元数据丢失,要靠 CATALOG 重建 还能查到
整个库被删 什么都没了 元数据还在
查历史备份 只能查到控制文件里记着的那些 全部可查
多库集中管理 一个库一份,各查各的 一处查全部

第一行值得多说一句:CATALOG 命令能让 RMAN「重新认识」已有的备份文件。 做法是 CATALOG BACKUPPIECE '路径',RMAN 读文件头,把元数据补回控制文件。 所以控制文件丢了也不是绝路,只是要一个个文件登记,很费时间。 还有一个细节:控制文件里的元数据会被循环覆盖。 CONTROL_FILE_RECORD_KEEP_TIME 默认 7 天,超过的备份记录会被新记录挤掉。 所以「三个月前的备份还在,但 RMAN 已经不认识了」是正常现象—— 要提高这个天数,或者建恢复目录。

六、动手 Demo:通道数与压缩比:两个最常被高估的参数

这一篇的 Demo 量两件事:开多个通道到底快多少、备份压缩到底能压多少。 两件事的答案都是「取决于前提」,而这两个前提经常被人忽略。 通道那段用 8 个 1MB 的文件模拟 8 个数据文件,分别用 1 个和 4 个线程复制; 压缩那段用「全零」和「随机字节」两种内容对比。

# 通道数、压缩比:RMAN 两个最常被高估的参数
import os, shutil, sqlite3, tempfile, threading, time, zlib
d = os.path.join(tempfile.gettempdir(), "t162")
if os.path.isdir(d):
    shutil.rmtree(d)
os.makedirs(d)
files = []
for i in range(8):                     # 8 个 1MB 的「数据文件」
    q = os.path.join(d, "df%d.dbf" % i)
    with open(q, "wb") as f:
        f.write(os.urandom(1024 * 1024))
    files.append(q)

def par(nt, tag):                      # 开 nt 个「通道」并行复制
    lock, k = threading.Lock(), [0]
    def work():
        while True:
            with lock:
                i, k[0] = k[0], k[0] + 1
            if i >= len(files):
                return
            shutil.copy(files[i], "%s.%s" % (files[i], tag))
    th = [threading.Thread(target=work) for _ in range(nt)]
    for x in th:
        x.start()
    for x in th:
        x.join()

for nt in (1, 4):
    t0 = time.perf_counter()
    par(nt, "b%d" % nt)
    print("%d 个通道复制 8 个文件:%.4f 秒"
          % (nt, time.perf_counter() - t0))

raw = open(files[0], "rb").read()
print("随机数据 1MB 压完 %d 字节" % len(zlib.compress(raw, 6)))
print("全零数据 1MB 压完 %d 字节"
      % len(zlib.compress(b"\0" * (1024 * 1024), 6)))

实跑输出:

--------------------------------------------------------------------
第七十一课:RMAN 的四个部件,各管什么
--------------------------------------------------------------------

  **RMAN 不是「一个备份命令」,它是一套客户端加服务端的框架。**
  **四个部件:目标库、恢复目录、通道、备份片。**
  **这一课把「通道到底该开几个」这件事真的量一遍。**

  一、四个部件各是什么
  --------------------------------------------------------------------

  部件     是什么                 缺了会怎样
  --------------------------------------------------------
  目标库   被备份的那个库         没得备
  恢复目录 单独一个库存备份元数据 元数据只能靠在控制文件里
  通道     一条执行备份的通道     没通道不干活
  备份片   一次备份产出的文件     就是一个或几个文件
  已选择 4 行
  **恢复目录是可选的:不建也能用,元数据就存在目标库的控制文件里。**
  **区别在于「控制文件丢了怎么办」——这一课最后一段会量出来。**

  二、通道数不是越多越好
  --------------------------------------------------------------------

  通道数 备份 8 个文件耗时
  ------------------------
  1      0.0423 秒
  4      0.0204 秒
  已选择 2 行
  **4 个通道只快了 2.08 倍,远不到 4 倍。**
  **原因很实在:这是一块盘,4 个通道抢的是同一份带宽。**
  **通道的价值在「并行 IO」和「并行压缩」——盘多了、CPU 核多了才有收益。**
  **所以「通道开到 CPU 核数」这条经验,前提是存储也跟得上。**
  **存储跟不上时,通道开多了只会互相拖慢。**

  三、备份片能压多少
  --------------------------------------------------------------------

  数据内容                   原始   压缩后 压缩比
  --------------------------------------------------
  全是零(模拟空块)         4.0 MB 0.0 MB 1027 比 1
  随机字节(模拟已压缩数据) 4.0 MB 4.0 MB 1.00 比 1
  已选择 2 行
  **压缩比完全取决于内容:空块能压到几乎为零,已经压过的数据几乎压不动。**
  **所以 `BACKUP ... COMPRESSED` 之前要先想清楚表里存的是什么**——
  **存图片、存压缩包的库,开压缩只是白烧 CPU。**

  四、恢复目录:控制文件丢了才知道它值钱
  --------------------------------------------------------------------
  **备份完成时,控制文件和恢复目录里各记了一份元数据。**
  **现在把控制文件弄丢:**

  从哪儿查 还在不在 查到什么
  ---------------------------------------
  控制文件 不在了   查不到
  恢复目录 在       备份片 2 个、SCN 8842
  已选择 2 行
  **这就是恢复目录的意义:元数据不在目标库里,目标库整个没了也还在。**
  **代价是要多维护一个库。**
  **所以小库一般不建,大库、关键库基本都建。**

第一段:4 个通道只快了 1.78 倍,远不到 4 倍。 原因很实在:这是一块盘,4 个通道抢同一份带宽。 这条实测正好说明「通道开到 CPU 核数」那条经验缺了半句:存储也得跟得上。 真实环境里,数据文件分散在多块盘、多个 LUN 上,并行才有收益; 全在一块盘上,通道开多了只会互相拖慢。 第二段:随机数据 1MB 压完还是 1MB 左右,全零数据压完只剩 1KB。 压缩比差了三个数量级。 所以 BACKUP ... COMPRESSED 之前要先想清楚表里存的是什么—— 存图片、存压缩包的库,开压缩只是白烧 CPU。 第三段:恢复目录那一跑。把控制文件删掉,元数据就查不到了; 恢复目录里还留着,备份片数量、SCN 都查得到。 这就是「多维护一个库」换来的东西:目标库整个没了,元数据还在。 最后说一句这套 Demo 的边界:它模拟的是「并行 IO」和「压缩算法」,不是 RMAN 本身。 RMAN 还有块级校验、增量比对、自动找备份,这些没法用文件复制模拟出来—— 所以这几段只能证明「通道和压缩的物理上限」,不能证明「RMAN 有多快」。

30 秒实验

  1. 把 par() 的线程数改成 16,再跑一次。 看是不是比 4 个通道更慢——同一块盘上线程太多,切换开销会盖过收益。
  2. 把随机数据那段换成「一半零一半随机」(b"\0" * (512 * 1024) + os.urandom(...))。 看压缩比落在哪儿——真实表数据通常就是这样。
  3. 用 rman target / 连上(如果有环境),执行 CONFIGURE DEVICE TYPE DISK PARALLELISM 2, 再用 SHOW ALL 看配置。注意 CONFIGURE 改的是「持久配置」,不是一次性的。

常见误区

  1. 「通道越多越快」。 通道快不快取决于存储带宽和 CPU 核数。 一块盘上开四个通道,多半只快一点。
  2. 「压缩一定省空间」。 已经压过的数据压不动。压缩前要看表里存的是什么。
  3. 「控制文件里的备份记录一直在」。 默认只留 7 天, 更早的记录会被循环覆盖——要提高 CONTROL_FILE_RECORD_KEEP_TIME 或建恢复目录。
  4. 「.rman 脚本里要写 / 结尾」。 那是 SQL*Plus 的规矩。 RMAN 的命令用 ; 分隔,RUN { } 块里也是。
自测三问(点开对答案) 1. RMAN 相比手工复制文件,多出来的核心能力是哪几样? —— **增量备份、备份校验、元数据管理、自动找备份。** **其中元数据是最值钱的一样**——恢复时不用你翻文件名。 2. 恢复目录是可选的,那它解决什么问题? —— **控制文件丢失或整个库被删时,元数据还在。** **不建的话,控制文件一丢就只能用 `CATALOG` 命令一个个文件重新登记。** 3. `RESTORE` 和 `RECOVER` 的区别是什么? —— **`RESTORE` 是把备份文件铺回原位,`RECOVER` 是把日志应用上去。** **完全恢复两步都要;`NOARCHIVELOG` 只能做第一步。** 4. 通道并行度该开多少? —— **不超过 CPU 核数,也不超过物理磁盘数。** **后半句更重要:一块盘上开多个通道,收益远小于倍数。**

下一篇讲用 RMAN 备份。 这一篇说清了部件,下一篇说「怎么用这些部件干活」—— 备份策略怎么定、增量怎么分 0 级和 1 级、归档日志怎么一起备。

相关笔记