逻辑结构讲「怎么组织」,物理结构讲「盘上放了什么」。 Oracle 的库由五类文件组成:数据文件、控制文件、重做日志、参数文件、密码/警告/跟踪文件。 少一个都起不来,但重要程度差得远。
上一篇把「文件里的东西」拆成四级逻辑单位,这一篇反过来,看盘上实实在在的那几个文件。 逻辑与物理不是两套东西,是同一个库的两个视角。 这一篇是第 7 章(控制文件与日志)和第 16 章(备份恢复)的引子。 那两章讲的每一件事,都落在这一篇列出的某类文件上。
一、五类文件,五种命
| 文件 | 记什么 | 丢了会怎样 |
|---|---|---|
数据文件 .dbf |
表、索引的真实数据 | 丢数据,但有备份加日志能重做 |
控制文件 .ctl |
库的骨架:有哪些文件、日志、SCN | 起不来,要靠备份或重建 |
重做日志 .log |
「改了什么」,用于恢复 | 最近的改动救不回来 |
| 参数文件 spfile/pfile | 内存怎么分、行为怎么定 | 起不来,得手写一份 |
| 密码/警告/跟踪文件 | 账号、告警、会话跟踪 | 能起来,但出事时没线索 |
这个排序很反直觉: 最不显眼的控制文件最要命(它才几 MB),最大的数据文件反而最好救。 原因很简单:控制文件描述「库是什么」,没有它实例就不知道去哪儿找数据; 数据文件描述「数据是什么」,有备份就能重建。
二、控制文件为什么最要命
它里面记着:
数据文件清单、重做日志清单、当前日志序列号、数据库名、创建时间、归档模式、检查点信息。
所以它必须是多份的。Oracle 默认就给你放两三份(control_files 参数里列几个就有几份),
写在不同的盘上,一份坏了用另一份。
第 7.1 节会讲怎么加、怎么恢复。
它还是个二进制小文件,通常只有几 MB——但它是唯一一个「丢了就得重建整个库」的文件。
三、重做日志:循环使用的账本
日志不是一个文件写到底,是一组文件轮流写。写满一个就切到下一个(日志切换), 切过去的那个如果开了归档,就拷一份出去留着。 所以日志的容量决定了「能回退多久」: 只有三组日志、每组 50 MB,写得又快,可能十分钟就转一圈—— 转一圈之后最早的日志就被覆盖了,那之前的改动就救不回来了。 这就是为什么生产库要开归档模式(7.3 节): 归档之后,被覆盖的日志已经在别处存了一份,随时能拿回来重做。
四、参数文件:spfile 优先于 pfile
pfile 是文本,能拿记事本改,改完要重启;
spfile 是二进制,只能 alter system 改,改完能立刻生效(看 scope)。
两者都存在时用 spfile。
这是「改了参数不生效」最常见的原因——改的是 initORCL.ora,系统读的是 spfileORCL.ora。
它们之间能互相生成:create pfile from spfile; 和 create spfile from pfile;。
spfile 坏了、库起不来的时候,这条命令是救命的那一步。
五、动手 Demo:在 SQLite 上找这五类文件的对应物
SQLite 只有一种文件:主库文件。 但它切到 WAL 模式后会多出两个,这三个文件正好把「数据文件/重做日志/共享内存」演全了。 控制文件它没有单独的,塞在文件头里——这一篇会直接把文件头那几十个字节读出来给你看。
import os, sqlite3
f = sqlite3.connect('phys.db')
f.execute('CREATE TABLE t (id INTEGER PRIMARY KEY, v TEXT)')
f.executemany('INSERT INTO t VALUES (?,?)', [(i, 'x' * 30) for i in range(1, 200)])
f.commit()
os.listdir('.') # ['phys.db']:只有一个文件
# 切成预写日志模式,多出 -wal 与 -shm
f.execute('PRAGMA journal_mode=WAL').fetchone()
f.execute("INSERT INTO t VALUES (999,'wal test')")
f.commit()
os.listdir('.') # phys.db / phys.db-wal / phys.db-shm
os.path.getsize('phys.db-wal') # WAL 攒了多少改动
# 控制文件:SQLite 把它塞在文件头里
head = open('phys.db','rb').read(100)
int.from_bytes(head[16:18], 'big') # 页大小 4096
int.from_bytes(head[28:32], 'big') # 页数
实跑输出:
--------------------------------------------------------------------
第八课:物理存储结构——盘上到底有哪几类文件
--------------------------------------------------------------------
**逻辑结构讲「怎么组织」,物理结构讲「盘上放了什么」。**
**Oracle 的库由五类文件组成,各有各的活,少一个都起不来。**
五类文件各管一件事
--------------------------------------------------------------------
SQL> select name, job from fl
NAME JOB
-----------------------------------------------------------
数据文件 .dbf 存表、索引的真实数据
控制文件 .ctl 记库的骨架:有哪些数据文件、日志、SCN
重做日志 .log 记「改了什么」,用于恢复与重做
参数文件 spfile/pfile 启动时按它分配内存、定行为
密码/警告/跟踪文件 账号口令、运行告警、会话跟踪
已选择 5 行
**重要程度排个序:**
**控制文件最要命**(没了库就不知道自己是啥)、**重做日志其次**(没了最近的改动就丢了)、
**数据文件反而最好救**(有备份加日志就能重做)。
在 SQLite 上找对应的文件
--------------------------------------------------------------------
**默认 journal 模式(回滚日志)**
phys.db 16384 字节
切成 WAL 模式,多出两个文件
--------------------------------------------------------------------
`PRAGMA journal_mode=WAL` → **wal**
**WAL 模式(预写日志)**
phys.db 16384 字节
phys.db-shm 32768 字节
phys.db-wal 4152 字节
**`-wal` 就是重做日志的对应物:**
**改动先写进它,再慢慢合并回主文件。** **`-shm` 是共享内存索引,给多进程读 WAL 用的。**
**看大小就知道 WAL 攒了多少改动**——
**Oracle 的 `v$log` 看的是同一件事:当前日志组写了多少、什么时候该切换。**
控制文件在 SQLite 里是哪一段
--------------------------------------------------------------------
**SQLite 没有单独的控制文件,它把「库的骨架」写在第 1 页的文件头里:**
**页大小、页数、schema 版本号、编码、文件变更计数。**
文件头前 16 字节(十六进制):**53514c69746520666f726d6174203300**
第 16~17 字节是页大小(大端):**4096**
第 28~31 字节是页数:**4**
**这三个数就相当于「控制文件里记的那点事」:块多大、库多大、结构什么样。**
**只不过 Oracle 把它单独存成文件、还给你多路复用(7.1 节讲),SQLite 直接塞在数据文件的头里。**
默认模式(rollback journal)下只有一个 .db 文件,
写的时候才临时出现一个 -journal,提交完就删掉。
这也是一种日志,只是用完即弃。
切成 WAL 之后多出 -wal 和 -shm:
-wal 是预写日志(改动先写它,再慢慢合并回主库),
-shm 是给多进程读 WAL 用的共享内存索引。
对应 Oracle:-wal 像重做日志,-shm 像 SGA 里的一块。
-wal 的大小就是「还没合并的改动有多少」——这个数会周期性回落(合并完就截断)。
Oracle 的 v$log 看的是同一件事:当前日志组写了多少。
文件头那三个数值得记:
前 16 字节是 SQLite format 3\0(魔数),第 16~17 字节是页大小,第 28~31 字节是页数。
Oracle 把这类信息单独存成控制文件,还给你多路复用;SQLite 图省事,直接写进数据文件第一页。
30 秒实验
- 把
journal_mode切回delete, 再看目录——-wal和-shm会自动消失。这说明它们是「模式带来的」,不是「库自带的」。 - 往
t里插 5000 行,再看-wal的大小。 它会涨得很大——这就是「日志攒着没合并」,对应 Oracle 里日志组快写满、要切换了。 - 用
dbstat(如果编进去了)或page_count估一下主库文件的实际数据占比,和文件大小比一比。 文件通常比数据大——因为空闲页留在里面没还。
常见误区
- 以为删掉
.dbf只是少一张表。 那是整个表空间的数据, 而且控制文件里还记着它——库会起不来,报ORA-01157。 - 以为日志文件可以随便删。 删当前日志组,实例直接崩; 删未归档的旧日志,那段时间的改动永远救不回来。
- 改了参数不生效,就去改 pfile。 先
show parameter spfile看一眼, 系统在用 spfile 的话,改 pfile 一点用都没有。
自测三问(点开对答案)
1. 五类文件里,哪个丢了最要命?为什么? —— **控制文件。** 它记着「这个库由哪些文件组成」,**没了它实例根本不知道去哪儿找数据**, 只能靠备份恢复或者重建。 2. 重做日志为什么要「循环使用」? —— **因为它会一直长**。写满就切、切了就覆盖最早的,**这样空间可控**; 代价是**覆盖之后的改动就救不回来了**,所以要开归档(7.3 节)。 3. `-wal` 文件对应 Oracle 的什么? —— **重做日志(redo)。** 都是「改动先写日志,再合并/刷到主文件」,**目的都是崩溃后能重做。**下一篇看「算」的那一半:SGA、PGA、前后台进程。 我会自己写一个 128 块的模拟盘加 LRU 缓存,把「缓冲池命中率」从一句口号量成一条曲线。
- ☐ (本篇完,下一篇 2.4 Oracle 服务器结构)