Oracle 的 startup 不是「打开一个文件」,它分三步:nomount、mount、open。
每一步能访问的东西都不一样,这直接决定了出问题时你能做什么。
关机也有四档,差别只在「等不等」。
这是第 1 章的最后一篇,也是第一次碰到「实例」这个概念。 上一篇说了三个入口,这一篇说的是入口打开之后,库处在什么状态。 这个三阶段模型是后面所有运维章节的地基:第 7 章改归档模式必须 mount、 第 16 章恢复要 mount、第 18 章闪回数据库也要 mount。不知道自己在哪一步,就不知道能敲什么。
一、三步开机:每步读一样东西
第一步,nomount:读参数文件,起实例。这一步只用到参数文件(spfile 或 pfile),
按里面的设置分配内存(SGA)、启动后台进程。
此时数据库还没被打开,只能查 v$instance 这类「实例级」视图。
第二步,mount:读控制文件。控制文件里记着数据文件、日志文件都在哪。
读完它,实例就知道「这个库由哪些文件组成」,但还没碰这些文件里的数据。
改归档模式、做恢复,都在这个状态下做。
第三步,open:打开数据文件和日志。这一步会校验文件头、检查一致性,
必要时做实例恢复(把日志重做一遍)。打开之后,数据字典可用,所有表都能查。
| 阶段 | 读了什么 | 能查什么 | 典型操作 |
|---|---|---|---|
| nomount | 参数文件 | v$instance |
建库、改内存参数 |
| mount | 控制文件 | v$database、v$log |
改归档模式、恢复 |
| open | 数据文件与日志 | 所有表 | 正常的增删改查 |
二、参数文件:spfile 和 pfile
pfile 是纯文本(initORCL.ora),能拿记事本改,改完要重启才生效。
spfile 是二进制(spfileORCL.ora),只能通过 alter system 改,改完可以只改内存、
只改文件、或者两边都改。
spfile 优先。 两个都存在时,Oracle 用 spfile。
这是很多人改完参数不生效的原因——他改的是 pfile,而系统在用的 spfile。
spfile 丢了怎么办? 能从这里救:
create pfile from spfile; 反向也能 create spfile from pfile;。
所以运维的第一条备份不是备份数据,是备份参数文件和控制文件——它们小,但没了就起不来。
三、四档关机:差别只在等不等
normal:不许新连接,等所有人自己断开。最安全,也最可能等一辈子。
transactional:不许新事务,等当前事务提交完就断。折中方案,实际用得不多。
immediate:回滚未提交事务、断开所有会话、写检查点,然后关。这是日常用的那一条。
abort:立刻停,不回滚、不写检查点。
相当于拔电。 下次启动时实例要做恢复(用重做日志把没写完的补上)。
abort 不是「更快」,是「把代价推给下一次启动」。
而且恢复不一定成功——如果日志文件本身坏了,那就真丢了。
所以它只在一种场景下用:库已经卡死,immediate 也关不掉。
四、起不来的时候看什么
第一看告警日志(alert log)。 它记着实例启动的每一步,
路径在 $ORACLE_BASE/diag/rdbms/<库名>/<实例名>/trace/alert_*.log。
起不来时,最后二三十行就是答案。
第二看两个常见错误码:ORA-01113(数据文件需要恢复)、ORA-01033(库正在启动或关闭)。
前者说明文件头不一致,要 recover;后者通常只是还没 open 完。
第三看服务。 Windows 上如果 OracleServiceORCL 没起,
sqlplus / as sysdba 会报 ORA-12560——这不是密码问题,是「根本没有实例在跑」。
五、动手 Demo:在 SQLite 上把三阶段跑一遍
SQLite 没有实例,所以严格说它没有 nomount 这回事。 但它有同样的层次:文件在不在 → 能不能读到结构 → 能不能查数据。 这三层正好对应「没连上 → mount → open」,拿来把概念演一遍最合适。 注意这只是类比,不是等价——差别在每一段的注释里。
import os, sqlite3, tempfile
path = os.path.join(tempfile.mkdtemp(), 'demo.db')
os.path.exists(path) # False:库还不存在
# 第一步(相当于 NOMOUNT):连上去,库还是空的
c = sqlite3.connect(path)
c.execute('select count(*) from sqlite_master').fetchone() # 0
c.execute('select * from emp').fetchall() # 报错
# 第二步(相当于 MOUNT):读到「有哪些对象」
c.execute("CREATE TABLE emp (empno INTEGER PRIMARY KEY, "
"ename TEXT, sal REAL)")
c.execute("select name from sqlite_master where type='table'")
# 第三步(相当于 OPEN):数据可读
c.execute('select empno, ename from emp').fetchall()
实跑输出:
--------------------------------------------------------------------
第五课:启动与关闭——三步开机,四档关机
--------------------------------------------------------------------
**Oracle 的 startup 不是「打开一个文件」那么简单,它分三步。**
**每一步能访问的东西都不一样,这决定了出问题时你能做什么。**
三步开机,每步能看到什么
--------------------------------------------------------------------
阶段 做了什么 能干什么
--------------------------------------------------------------------------------------
NOMOUNT 读参数文件、起实例、分配 SGA 能查 v$instance,查不了任何表
MOUNT 读控制文件,知道数据文件在哪 能查 v$database、能改归档模式
OPEN 打开数据文件与重做日志,字典可用 能查所有表
**这三步对应三条命令:**
[Oracle 原码 · 本沙箱不执行]
| STARTUP NOMOUNT; -- 只到第一步
| ALTER DATABASE MOUNT; -- 到第二步
| ALTER DATABASE OPEN; -- 到第三步
| STARTUP; -- 一步到 OPEN(最常用)
在 SQLite 上把这三步演一遍
--------------------------------------------------------------------
**SQLite 没有实例,但它也有「打开了才知道」的层次:**
**文件在不在 → 能不能读结构 → 能不能查数据。**
临时库路径:`C:/Users/admin/AppData/Local/Temp/ora47_6stm08q6/demo.db`
**第一步之前:文件还没有。**
`os.path.exists(path)` → **False**
**但这儿有个 SQLite 的脾气:连一个不存在的路径,它会顺手建一个空库。**
硬连上去查结构 → **成功(0 个对象)**,文件当场被建了出来
`os.path.exists(path)` 再看一次 → **True**
**第一步(相当于 NOMOUNT):建实例,有内存,但库还是空的。**
`select count(*) from sqlite_master` → **0**
直接查业务表 → **报错 `no such table: emp`**
**这就是「实例起来了、库还没打开」的样子:**
**内存有了,字典里空空如也,查业务表必然找不到。**
**第二步(相当于 MOUNT):读到控制文件,知道有哪些文件。**
SQL> select * from sqlite_master where type = 'table'
TYPE NAME TBL_NAME ROOTPAGE SQL
-----------------------------------------------------------------------------------------------
table emp emp 2 CREATE TABLE emp (empno INTEGER PRIMARY KEY, ename TEXT, sal REAL)
已选择 1 行
**SQLite 的 `sqlite_master` 就等于 Oracle 的控制文件加数据字典:**
**它记着「这个库里有什么」。**
**第三步(相当于 OPEN):数据可读可写。**
SQL> select empno, ename, sal from emp
EMPNO ENAME SAL
---------------
7369 SMITH 800
已选择 1 行
四档关机,差别在「等不等」
--------------------------------------------------------------------
SQL> select mode, wait, scene from shut
MODE WAIT SCENE
-----------------------------------------------------------------------
normal 等所有人自己断开 不赶时间,最安全
transactional 等当前事务提交完 想快点又不想丢事务
immediate 回滚未提交事务,断开所有人 最常用
abort 立刻停,不回滚、不写检查点 只有卡死时才用,下次启动要恢复
已选择 4 行
[Oracle 原码 · 本沙箱不执行]
| SHUTDOWN IMMEDIATE; -- 日常用这一条
| SHUTDOWN ABORT; -- 最后手段,起来后要做实例恢复
**`abort` 相当于拔电。**
**Oracle 靠重做日志把它救回来,但救的过程要时间,而且救不回来的部分就没了。**
**所以它不是「更快」,是「把代价推给下一次启动」。**
收个尾:这一章真正该记住的三句
--------------------------------------------------------------------
- **实例和数据库是两回事**,一个是内存加进程,一个是盘上的文件。
- **三范式是为了少改几行**,不是越碎越好。
- **startup 三步、shutdown 四档**,出问题时你在哪一步,决定了你能做什么。
**后面五章讲的所有东西,都挂在这三句上。**
第一段有个反直觉的地方: 文件还不存在,可 sqlite3.connect(path) 却成功了,
而且顺手把文件建了出来。
SQLite 的设计是「连接即创建」,Oracle 完全不是这样——连不上就是连不上,不会替你建库。
第二段是真正的「nomount 类比」:库连上了、内存有了,但 sqlite_master 是空的,
查业务表直接报 no such table。这就是「实例起来了、库还没打开」的样子。
第三段对应 mount: 建完表再看 sqlite_master,能看到「这个库里有什么」。
SQLite 的 sqlite_master 一个人干了两件事——
既是控制文件(记文件结构),又是数据字典(记表结构)。
Oracle 里这是两个东西,分别在第 2 章和第 7 章讲。
最后一段对应 open: 数据可读可写。
注意 SQLite 没有「校验文件头、做实例恢复」这一步——
它出问题时的默认动作是「尽量读出能读的部分」,不是「拒绝打开」。
这个差别很重要:Oracle 宁可打不开,也不给你不一致的数据。
30 秒实验
- 把
sqlite3.connect(path)改成sqlite3.connect('file:' + path + '?mode=ro', uri=True), 再试一次写入。 只读打开会报attempt to write a readonly database—— 这就是「库打开了但不让写」,对应 Oracle 的 read only 表空间。 - 在
sqlite3.connect之后立刻c.close(),再重新连一次,看数据还在不在。 在的——SQLite 每次 commit 就落盘,没有「实例内存里还没写」这回事。 - 去翻一翻
PRAGMA journal_mode的取值(delete/wal/memory)。wal就是 SQLite 的重做日志,对应第 7 章要讲的 redo。
常见误区
- 以为
startup就是「打开数据库」。 它其实是三个动作连做,中间每一步都可能失败, 失败在哪一步决定了你怎么救。 - 用
shutdown abort当成「快速关机」。 它不回滚、不写检查点, 下次启动要做恢复,恢复期间库是不可用的。 日常一律用immediate。 - 改完参数不生效,就以为参数写错了。 十有八九是改的是 pfile,系统在用的却是 spfile。
先
show parameter spfile确认一下。
自测三问(点开对答案)
1. 要改归档模式,库至少要开到哪一步? —— **mount。** 归档模式记在控制文件里,**必须在读得到控制文件、又还没打开数据文件的状态下改**, 也就是 `startup mount` 之后。 2. `shutdown abort` 之后重启,Oracle 会做什么? —— **做实例恢复:** 用重做日志把已提交但没写进数据文件的事务重做一遍,把未提交的回滚掉。 **这一步是自动的,但库在恢复完之前不能用。** 3. `ORA-12560` 一般是哪一层的问题? —— **不是密码、不是权限,是「没有实例在跑」**——多半是 Windows 服务 `OracleServiceORCL` 没启动, 或者 `ORACLE_SID` 指错了。第 1 章到这里结束。 下一篇进第 2 章:Oracle 的体系结构——把「实例」和「数据库」彻底分开讲, 再往下拆成逻辑存储(块/区/段/表空间)和物理存储(五类文件)。
- ☐ (本篇完,下一篇 2.1 Oracle 体系结构概述)
相关笔记
- Oracle 19c 的管理工具
-
- 🗄️ 数据库基础/00-索引
- 🏺 Oracle从入门到精通 · 索引