🏺 第14章 Oracle 系统调优
📄 一句话概括

参数有两份:内存里一份(马上生效、重启就没了),spfile 里一份(重启才生效、一直在)。 SCOPE 就是用来选改哪一份的。 改参数最常见的两个事故:改了 SCOPE=MEMORY 重启后「白改了」,和改错了没法回退。 所以这一篇的重点不是语法,是「改完怎么确认」。

📝 本章和前面几章的关系

第 13 章讲的是「别出错」。 从这一篇开始讲「跑得快」—— 调优的第一步永远是「把参数调对」,因为参数错了,后面怎么优化 SQL 都白费。 这一篇是第 14 章的地基: 14.2 讲 SGA 的几块内存怎么分,14.3 讲排序区怎么调,而「怎么改」这件事只在这一篇讲一次。

一、先分清两类参数

分类 例子 能不能改
初始化参数 SGA_TARGET、PROCESSES 可以,看是否静态
隐藏参数 _lm_dd_interval 能改,但别碰

隐藏参数名字以下划线开头,官方文档里查不到。 它们是给 Oracle 自己用的,改错了可能起不来,而且升级后行为会变。 只有一种情况值得碰:官方支持明确让你临时设一个来绕过 bug。 初始化参数还能再分两类,这个分类更实用: 「动态参数」改完立刻生效,「静态参数」只能写进 spfile、重启才生效。 DB_BLOCK_SIZE、DB_NAME、SGA_MAX_SIZE 都是静态的。

二、pfile 和 spfile:一个文本、一个二进制

pfile spfile
形式 文本,能拿记事本改 二进制,只能 ALTER SYSTEM 改
默认名 init<sid>.ora spfile<sid>.ora
位置 $ORACLE_HOME/dbs $ORACLE_HOME/dbs
谁优先 两个都在时,spfile 优先 ——

spfile 优先,这是 9i 之后的行为。 所以「我改了 pfile 怎么不生效」的答案通常是:目录里还有个 spfile,它把你的 pfile 盖住了。 spfile 的好处是「不会写错语法」——文本文件里多一个空格、少一个引号,实例可能就起不来。 spfile 由数据库自己维护,格式永远是对的。 坏处是「出问题时改不动」—— 起不来的实例没法执行 ALTER SYSTEM,只能先用 pfile 起来,再重建 spfile。

三、SCOPE 三个值,选错就白改

SCOPE= 内存 spfile 什么时候用
MEMORY 改 不改 临时试一下
SPFILE 不改 改 静态参数只能这么改
BOTH(默认) 改 改 常规改法

默认是 BOTH,所以大多数时候你什么都不用写。 但有两个例外必须显式写: 一是静态参数(写 BOTH 会报 ORA-02095,只能写 SPFILE), 二是「只想临时试试」(写 MEMORY)。 注意 SCOPE=MEMORY 的坑:改完当时是好的,重启就回去了。 而且它不写警告,你就这么忘了。 所以生产上要养成习惯:改完立刻 SHOW PARAMETER 看一眼, 再查一次 V$SPPARAMETER 确认磁盘上也改了。 一个看内存,一个看文件,两个都对上才算改完。

四、主要参数:先记住这七个

参数 管什么 怎么定
SGA_TARGET SGA 总大小,自动分给各池 物理内存的一半左右
PGA_AGGREGATE_TARGET 所有会话的排序、哈希内存总和 物理内存的 20% 上下
DB_CACHE_SIZE 缓冲池(放数据块) 设 0 交给自动管理
SHARED_POOL_SIZE 共享池(放 SQL 和字典) 设 0 交给自动管理
LOG_BUFFER 日志缓冲区 几兆到几十兆,别大
PROCESSES 最大进程数(连带着 SESSIONS) 按连接数上限留余量
OPEN_CURSORS 一个会话最多开几个游标 默认 300,一般够

前两个是总闸,后面两个(DB_CACHE_SIZE、SHARED_POOL_SIZE)建议留 0。 留 0 不是「不分配」,是「交给 SGA_TARGET 自动分」——这叫自动共享内存管理(ASMM)。 手动指定这两个数,等于把自动分配关掉一部分,通常没必要。 PROCESSES 有个连带关系要知道:改它,SESSIONS 会按 1.1 倍自动跟着调。 反过来只改 SESSIONS 也行,但两边不一致的时候可能报 ORA-00020。 所以连接数不够,改 PROCESSES。

五、改参数的规矩:四条

一、一次只改一个,改完观察。 同时改五个,出问题了你不知道是哪个引起的。 二、改之前把原值记下来。 SHOW PARAMETER 的结果存一份,回退的时候照着改回去。 ALTER SYSTEM RESET 能回默认值,但默认值随版本变,不是你想要的那个旧值。 三、挑时间改。 改 SGA_TARGET 会导致内存重新分配,短时间性能会抖一下。 改 PROCESSES 要重启。 这类操作别在大促当天做。 四、改完写进变更记录。 「谁在什么时候把 SGA_TARGET 从 2G 改成 4G、为什么」——半年后有人问起,只有记录能回答。 参数漂移是运维里最难查的一类问题:没人记得改过,但行为就是和文档不一样。

六、动手 Demo:两份参数值,一次重启,看清 SCOPE 的作用

参数管理这件事没法在 SQLite 上验,所以这一篇的 Demo 是一个手写的参数文件模型。 它照着 Oracle 的规矩来:磁盘一份、内存一份,SCOPE 决定改哪份,静态参数只让写文件。 模型的输出就是每一次「内存值 vs 磁盘值」的对照表。 看懂这张表,SCOPE 就不会再用错。

# 参数文件模型:内存一份、磁盘一份
import os, tempfile
P = os.path.join(tempfile.gettempdir(), "spfile47.ora")

class ParamFile(object):
    STATIC = ("db_block_size", "db_name", "sga_max_size")

    def __init__(self, path):
        self.path = path
        self.spfile = {"sga_target": "2G", "processes": "300",
                       "db_block_size": "8K"}
        self.mem = {}
        self.flush()

    def flush(self):                       # 写磁盘
        with open(self.path, "w") as f:
            for k in sorted(self.spfile):
                f.write("%s=%s\n" % (k, self.spfile[k]))

    def startup(self):                     # 启动:读磁盘进内存
        self.mem = {}
        with open(self.path) as f:
            for ln in f:
                k, v = ln.strip().split("=", 1)
                self.mem[k] = v

    def alter(self, name, value, scope="BOTH"):
        if name in self.STATIC and scope != "SPFILE":
            return "ORA-02095: 无法修改指定的初始化参数"
        if scope in ("BOTH", "MEMORY"):
            self.mem[name] = value
        if scope in ("BOTH", "SPFILE"):
            self.spfile[name] = value
            self.flush()
        return "系统已更改"

db = ParamFile(P)
db.startup()
print(db.alter("sga_target", "4G", "MEMORY"))
print("内存", db.mem["sga_target"], "磁盘", db.spfile["sga_target"])
db.startup()                            # 重启
print("重启后内存", db.mem["sga_target"])   # 回到 2G

实跑输出:

--------------------------------------------------------------------
第六十一课:参数文件——内存一份,磁盘一份
--------------------------------------------------------------------

  **改参数这件事最容易搞错的不是语法,是「改哪一份」。**
  **内存有一份(马上生效、重启就没),磁盘有一份(重启后才生效、但一直留着)。**
  **这一课把这两份的差别用一个能真跑的模型摆清楚。**

  一、启动的时候读的是 spfile
  --------------------------------------------------------------------
  **实例启动:把 spfile 里的键值读进内存。**

  参数                 内存值 SPFILE 值
  -------------------------------------
  sga_target           2G     2G
  pga_aggregate_target 512M   512M
  db_cache_size        0      0
  shared_pool_size     0      0
  已选择 4 行
  **两份完全一样,因为它们就是一次拷贝。**
  **从这里开始,两份就可能分叉了。**

  二、SCOPE=MEMORY:只管这一会儿
  --------------------------------------------------------------------
  `ALTER SYSTEM SET sga_target=4G SCOPE=MEMORY;` 返回:系统已更改

  参数       内存值 SPFILE 值
  ---------------------------
  sga_target 4G     2G
  已选择 1 行
  **内存里是 4G,磁盘上还是 2G。**
  **这意味着「当前跑得欢,重启就回去」——**
  **临时试一试的时候用它,试完记得改回去。**

  三、重启一次,看看丢了什么
  --------------------------------------------------------------------

  参数       内存值 SPFILE 值
  ---------------------------
  sga_target 2G     2G
  已选择 1 行
  **果然回到 2G。**
  **所以「我明明改过了,怎么又变回去了」这个问题,答案永远是「你只改了内存」。**

  四、SCOPE=SPFILE:现在不动,重启才动
  --------------------------------------------------------------------
  `ALTER SYSTEM SET processes=600 SCOPE=SPFILE;` 返回:系统已更改

  参数      内存值 SPFILE 值
  --------------------------
  processes 300    600
  已选择 1 行
  **内存里还是 300,磁盘上已经 600。**
  **改完这句、重启之前,新连接还是按 300 算。**
  **`PROCESSES` 这类参数不是「动态」的,只能这么改。**

  参数      内存值 SPFILE 值
  --------------------------
  processes 600    600
  已选择 1 行
  **重启之后 600 生效了。**

  五、静态参数:连 SCOPE=BOTH 都不让用
  --------------------------------------------------------------------
  `ALTER SYSTEM SET db_block_size=16K;` 返回:ORA-02095: 无法修改指定的初始化参数
  带上 `SCOPE=SPFILE` 再试:系统已更改
  **注意第二句「成功」只是写进了文件。**
  **不重启,数据库还是 8K 的块。**
  **`DB_BLOCK_SIZE` 是建库时定死的,理论上改 spfile 也没用**——
  **它只在建库那一刻生效,之后改它就是「自欺欺人」。**

  六、改错了想回去:RESET
  --------------------------------------------------------------------

  动作                         内存值 SPFILE 值
  ----------------------------------------------
  改之前                       300    600
  ALTER SYSTEM RESET processes 300    (默认值)
  已选择 2 行
  **`ALTER SYSTEM RESET processes;` 把参数从 spfile 里删掉,回到默认值。**
  **比手写一个「默认值」更稳**——**因为默认值随版本会变,你写死的那个数未必还是默认。**
  **这一课一共把 spfile 重写了 4 次(算上建文件那一次)。**
  **每改一个参数,整个文件都要重写一遍,所以别把它写成循环。**

第一段:启动时两份一样。 这不是巧合,实例启动就是「把文件读进内存」。 第二段:SCOPE=MEMORY 之后,内存 4G、磁盘 2G。 这一行是整篇最该记住的:改完当时是有效的,重启就回到 2G。 第三段:重启验证。 「我明明改过」这句话,九成出在这一步。 第四段:SCOPE=SPFILE 反过来——内存还是 300,磁盘已经 600。 这句改完、重启之前,新连接还是按 300 算。 所以「改完没反应」也可能是对的,只是还没重启。 第五段:静态参数写 BOTH 报 ORA-02095,写 SPFILE 才通过。 注意这个「通过」只是写进了文件,DB_BLOCK_SIZE 实际上建库时就定死了,改它没有意义。 第六段:RESET 是回默认值,不是回旧值。 想回旧值只有一个办法:改之前记下来。

30 秒实验

  1. 给模型加一个 scope=u"INVALID" 的分支,让它报 ORA-02065(非法选项)。 想想真库里这个错一般是怎么来的。
  2. 在模型里把 processes 也加进 STATIC,再跑一遍。 你会看到 SCOPE=BOTH 直接被拒——这就是静态参数的待遇。
  3. 把 flush() 换成「追加写一行」而不是「重写整个文件」,再数一数写文件的次数。 真 spfile 是整块重写的,这也是「别批量改参数」的原因。

常见误区

  1. 「改完 SHOW PARAMETER 是新的就好了」。 不一定。 那个命令看的是内存值。要确认磁盘,得查 V$SPPARAMETER。
  2. 「spfile 能用文本编辑器改」。 不能,它是二进制。 要改只能 ALTER SYSTEM,或者先导成 pfile 改完再导回去。
  3. 「参数越大越快」。 反例很多:LOG_BUFFER 大了提交变慢, SORT_AREA_SIZE 大了几十个会话一起来就 OOM。
  4. 「隐藏参数调一调性能就上去了」。 大多数时候是「调完看着快了,其实别的地方坏了」。 除非官方文档或补丁说明明确让你设,否则别动。
自测三问(点开对答案) 1. `SCOPE=MEMORY` 和 `SCOPE=SPFILE` 分别改哪里? —— **前者只改内存(重启丢失),后者只改文件(重启生效)。** **`BOTH` 是两边都改,也是默认值。** 2. 静态参数为什么不能写 `SCOPE=BOTH`? —— **因为内存里改不了**,只能写进文件等重启,所以会报 `ORA-02095`。 3. 改完参数怎么确认真的改上了? —— **两个地方都看:`SHOW PARAMETER` 看内存,`V$SPPARAMETER` 看文件。** 4. pfile 和 spfile 同时在,用哪个? —— **spfile 优先。** 想让 pfile 生效,得先把 spfile 挪走或者改名。

下一篇讲 SGA。参数是「怎么改」,SGA 是「改什么」。 这一篇的表里 SGA_TARGET 排第一,下一篇就把这块内存拆开看:缓冲池、共享池、 日志缓冲区各占多少、各管什么。

相关笔记