こんにちは、おうどんです🍜
新人さん「先週、OPEN_CURSORS を 400 に上げましたよね」
おうどん「上げたね。ちゃんと確認もしたよね」
新人さん「日曜のメンテで再起動したら、300 に戻ってました」
おうどん「……え」
新人さん「あと、誰も触ってないはずの設定が、再起動したら1個変わってました」
おうどん「……え?」
(会話は説明用の架空の場面です)
戻るはずのない設定が戻る。
変わるはずのない設定が変わる。
どっちも、再起動のタイミングで。
これ、おうどんも何度か「なんで?」と迷ったことがある疑問です。
犯人は、たいてい ALTER SYSTEM の SCOPE なんです。
うどん屋の厨房でたとえると、Oracle には書き込み先が2つあります。
- 今日の鍋:いま煮えている出汁。味を変えたら、すぐにお客さんに出る。でも、閉店したら捨てる
- レシピ帳:大将が毎朝、仕込みのときに読むノート。書き換えても、今日の鍋の味は変わらない
大将(Oracle)「毎朝、レシピ帳どおりに作ります! 一字一句、そのとおりに!」
……忠実すぎて、昨日の鍋に足したしょうゆのことは、きれいに忘れてくれる大将です。
最初に、初心者向けのチェックリストです。
- 再起動のあとも残したい変更は、
SCOPE = BOTH(鍋とレシピ帳の両方)で変える - 変えたあとは「いまの値」と「再起動後の値」を、両方とも確かめる
- 再起動の前に、鍋とレシピ帳の食い違いを一覧にして見る(この記事の差分 SQL)
この記事の検証は、今回の検証環境(Oracle Database 19c 19.3、シングルインスタンス・CDB 構成、SPFILE で起動)で、SQL*Plus から SYSDBA で実行したものです。インスタンスの再起動も実際に行って確かめています。
SCOPE は「どこに書き込むか」の指定
結論から言うと、SCOPE は「鍋に入れるか、レシピ帳に書くか、両方か」を選ぶ言葉です。
ALTER SYSTEM のリファレンスと管理者ガイドの初期化パラメータの章には、こう書かれています。
| SCOPE | 書き込み先 | いつ効く | 再起動したら |
|---|---|---|---|
MEMORY | メモリだけ(今日の鍋) | 動的パラメータはすぐ(DEFERRED 指定時は新しいセッションから) | 消える |
SPFILE | SPFILE だけ(レシピ帳) | 次の起動から(動的・静的どちらのパラメータでも) | 効きはじめる |
BOTH | 両方 | 動的パラメータはすぐ(DEFERRED 指定時は新しいセッションから) | 残る |
用語を少しだけ。
- 初期化パラメータ:Oracle の動き方を決める設定値(OPEN_CURSORS など)
- SPFILE(サーバー・パラメータ・ファイル):初期化パラメータを保存しておくバイナリのファイル。起動するときに読まれます
- PFILE:同じ役目のテキストファイル版。
init<SID>.oraという名前が多いです - 動的パラメータ:動いている最中に変えられるもの。パラメータによっては、新しいセッションから反映する
DEFERRED指定が必要です。静的パラメータは、変えても再起動するまで効かないもの
SCOPE を書かなかったときの既定値は、SPFILE で起動していれば BOTH、PFILE で起動していれば MEMORY です。
PFILE で起動しているときは MEMORY しか指定できず、管理者ガイドには SPFILE や BOTH を指定するとエラーになる、と書かれています。
新人さん「じゃあ、いつも SCOPE を書かなければ、勝手に BOTH になって安心ですね!」
おうどん「PFILE で起動している DB で同じことをすると、勝手に MEMORY になるけどね」
……同じ1行が、DB によって鍋だけになったりレシピ帳込みになったりする。省略は、DB の起動方法に運命を預ける行為です。
静的パラメータ(PROCESSES など)は、SPFILE しか指定できません。このあたりの「そもそも再起動が要るか」の見分け方は、Oracleの初期化パラメータ変更で再起動が必要か調べる方法にまとめました。
この記事は、その次の疑問。「変えた値が、再起動のあとどうなるか」の話です。
大将「レシピ帳に書いてないことは、明日の私は知りません」
おうどん「潔い」
……潔いけど、そのぶん、レシピ帳に書かなかった人の責任が重い。
3つの SCOPE で変えて、実際に再起動してみた
百聞は一見にしかず。鍋だけ・レシピ帳だけの変更を入れて、本当に再起動してみました。
使ったのは、どれも動的なパラメータです。
- OPEN_CURSORS:1セッションが同時に開けるカーソル数の上限。SPFILE には 300 と書いてある
- OPTIMIZER_INDEX_CACHING:オプティマイザ(SQL の実行方法を決める係)向けの設定。SPFILE には書いていない(既定値 0)
- OPTIMIZER_INDEX_COST_ADJ:同じくオプティマイザ向けの設定。SPFILE には書いていない(既定値 100)
alter system set open_cursors = 400 scope = memory;
alter system set optimizer_index_caching = 10 scope = memory;
alter system set optimizer_index_cost_adj = 50 comment = 'test 2026-10-14 reset after' scope = spfile;
System altered.
System altered.
System altered.
3つとも成功。
では、いまのインスタンスの値(V$SYSTEM_PARAMETER)を見ます。
select name, value, isdefault, ismodified from v$system_parameter
where con_id = 0 and name in ('open_cursors','optimizer_index_caching','optimizer_index_cost_adj') order by name;
NAME VALUE ISDEFAULT ISMODIFIED
-------------------------- -------------- --------- ----------
open_cursors 400 FALSE MODIFIED
optimizer_index_caching 10 TRUE MODIFIED
optimizer_index_cost_adj 100 TRUE FALSE
3 rows selected.
(今回の検証環境の CDB$ROOT で、上の3つの ALTER SYSTEM の直後に実行した結果です。列幅は SQL*Plus の col で調整しています)
鍋に入れた2つは、すぐ変わっています。
レシピ帳にだけ書いた optimizer_index_cost_adj は、100 のまま。ISMODIFIED も FALSE です。いまの鍋には、何も起きていません。
レシピ帳(V$SPPARAMETER)のほうを見ると。
select name, value, update_comment from v$spparameter where name = 'optimizer_index_cost_adj';
NAME VALUE UPDATE_COMMENT
-------------------------- -------------- ----------------------------------------
optimizer_index_cost_adj 50 test 2026-10-14 reset after
1 row selected.
ちゃんと 50 と、付箋(COMMENT)が書いてあります。
新人さん「System altered. が出たのに、値が 100 のままです。失敗ですか?」
おうどん「成功だよ。レシピ帳にだけ書いたから、鍋は明日から」
新人さん「……今日の鍋、誰が責任とるんですか」
……今日の鍋は、今日の鍋のまま。それが SCOPE = SPFILE の約束です。
では、再起動します。
shutdown immediate
startup
Database closed.
Database dismounted.
ORACLE instance shut down.
ORACLE instance started.
Total System Global Area 2147483144 bytes
Fixed Size 9268744 bytes
Variable Size 486539264 bytes
Database Buffers 1644167168 bytes
Redo Buffers 7507968 bytes
Database mounted.
Database opened.
再起動のあと、さっきと同じ SELECT を流しました。
NAME VALUE ISDEFAULT ISMODIFIED
-------------------------- -------------- --------- ----------
open_cursors 300 FALSE FALSE
optimizer_index_caching 0 TRUE FALSE
optimizer_index_cost_adj 50 FALSE FALSE
3 rows selected.
(今回の検証環境で、再起動の直後に新しく接続して実行した結果です)
| パラメータ | 変え方 | 再起動の前 | 再起動の後 |
|---|---|---|---|
| open_cursors | MEMORY で 400 | 400 | 300 に戻った |
| optimizer_index_caching | MEMORY で 10 | 10 | 0(既定値)に戻った |
| optimizer_index_cost_adj | SPFILE で 50 | 100 | 50 に変わった |
冒頭の新人さんの2つの「え?」が、そのまま再現しました。
- 戻った設定 →
SCOPE = MEMORYで変えていた(鍋にしょうゆを足しただけ) - 誰も触っていないのに変わった設定 → 前に誰かが
SCOPE = SPFILEで書いていた(レシピ帳の書き込みが、次の仕込みで初めて効いた)
大将「おはようございます! レシピ帳どおりに作りました!」
おうどん「昨日の鍋に足した、しょうゆは?」
大将「レシピ帳に書いてなかったので、知りません!」
……悪気が一切ないのが、いちばん困る。
特に怖いのは2つ目です。レシピ帳だけの書き込みは、書いた日には何も起きない。
3か月後の計画停止で、誰も覚えていない変更が急に効きはじめる。いわば時限式の書き込みです。
新人さん「じゃあ、SCOPE = SPFILE は使っちゃダメなんですね」
おうどん「静的パラメータは SPFILE でしか変えられないから、ダメではない。使うなら、次の再起動の作業計画に書いておく」
……時限装置を仕掛けるなら、せめて仕掛けたことは引き継ごう。
BOTH の使い方と、戻し方の落とし穴
いまも、再起動のあとも同じ値にしたいなら、SCOPE = BOTH です。鍋にも入れて、レシピ帳にも書く。
ここまでは素直なんですが、元に戻すときに落とし穴がありました。
もともと SPFILE に書いていなかった optimizer_index_caching を、BOTH で 10 にして、BOTH で 0(既定値)に戻してみます。
select count(*) specified from v$spparameter where isspecified = 'TRUE';
alter system set optimizer_index_caching = 10 scope = both;
select name, value, isspecified from v$spparameter where name = 'optimizer_index_caching';
alter system set optimizer_index_caching = 0 scope = both;
select name, value, isspecified from v$spparameter where name = 'optimizer_index_caching';
select count(*) specified from v$spparameter where isspecified = 'TRUE';
SPECIFIED
----------
19
1 row selected.
System altered.
NAME VALUE ISSPECIFIED
-------------------------- -------------- -----------
optimizer_index_caching 10 TRUE
1 row selected.
System altered.
NAME VALUE ISSPECIFIED
-------------------------- -------------- -----------
optimizer_index_caching 0 TRUE
1 row selected.
SPECIFIED
----------
20
1 row selected.
値は元の 0 に戻りました。
でも、SPFILE に書かれている行は 19 から 20 に増えています。「0 です」という行が、レシピ帳に新しく書き足されたんです。
大将「しょうゆ大さじ0、と書いておきました!」
おうどん「……それ、書かなくても同じじゃない?」
大将「書いてあるほうが、安心します!」
……安心するのは大将だけで、あとでレシピ帳を読む人は「なんでわざわざ0?」と悩みます。
今日の値は同じでも、困ることがあります。既定値は、バージョンを上げたときなどに変わることがあるからです。レシピ帳に「0」と書いてあると、新しい版の既定値ではなく、0 が使われ続けます(今回の optimizer_index_caching の既定値が将来変わる、という意味ではありません。書いてあるものは書いてあるとおりに使われる、という一般論です)。
元の状態に戻すには、SPFILE から行を消す ALTER SYSTEM RESET を使います。
alter system reset optimizer_index_caching scope = spfile;
select name, value, isspecified from v$spparameter where name = 'optimizer_index_caching';
select count(*) specified from v$spparameter where isspecified = 'TRUE';
select name, value from v$system_parameter where con_id = 0 and name = 'optimizer_index_caching';
System altered.
NAME VALUE ISSPECIFIED
-------------------------- -------------- -----------
optimizer_index_caching FALSE
1 row selected.
SPECIFIED
----------
19
1 row selected.
NAME VALUE
-------------------------- --------------
optimizer_index_caching 0
1 row selected.
(この章の2つのブロックは、今回の検証環境で続けて実行した結果です。あいだで、あとで紹介する差分 SQL も流していますが、ここでは省いています。列幅は col で調整しています)
19 行に戻りました。
戻し方は、変える前の状態で決まります。
| 変える前の SPFILE | 戻し方 |
|---|---|
| そのパラメータが書いてあった | 元の値を SCOPE = BOTH で設定し直す |
書いてなかった(ISSPECIFIED = FALSE) | ALTER SYSTEM RESET <名前> SCOPE = SPFILE で行を消し、いまの値は元の値を SCOPE = MEMORY で戻す |
だから、変える前に V$SPPARAMETER の ISSPECIFIED を見ておく。これが戻し方を決める一行です。
新人さん「戻し方まで、変える前に決めるんですか」
おうどん「戻すときに初めて考えると、だいたい慌てて BOTH で上書きして、20 行目が増える」
……レシピ帳が、反省文のように少しずつ厚くなっていく。
🔰 ここまで読めば今日から困らない
MEMORYは今だけ。再起動で消えるSPFILEは次の起動から。今は何も変わらない(静的パラメータはこれしか使えない)BOTHは今も、再起動のあとも。SPFILE で起動しているときの既定値- 変えたら、いまの値は
V$SYSTEM_PARAMETER、再起動後の値はV$SPPARAMETERで確かめる - 戻し方は、変える前の
ISSPECIFIEDで決まる。書いてなかったものはRESETで消す
ここから先は中級者向け。再起動の前に、鍋とレシピ帳の食い違いを全部洗い出す SQL と、作業手順です。
再起動の前に流す差分 SQL(完成版)
冒頭の事故は、どちらも「鍋とレシピ帳が食い違ったまま、再起動した」のが原因でした。
だったら、再起動の前に食い違いを一覧にすればいい。
いまの値は V$SYSTEM_PARAMETER2、SPFILE の中身は V$SPPARAMETER です。この2つを突き合わせます。
-- memory (now) vs spfile (after restart)
set lines 200 pages 200 tab off feedback on
col name for a26
col ord for 99
col memory_now for a14
col after_restart for a14
col note for a40
select nvl(m.name, s.name) name,
nvl(m.ordinal, s.ordinal) ord,
m.value memory_now,
s.value after_restart,
case
when s.name is null then 'memory only: lost at restart'
when m.name is null then 'spfile only'
else 'differs: changes at restart'
end note
from (select name, ordinal, value, isdefault, ismodified
from v$system_parameter2
where con_id = 0) m
full outer join
(select name, ordinal, value
from v$spparameter
where isspecified = 'TRUE'
and sid in ('*', sys_context('USERENV', 'INSTANCE_NAME'))) s
on s.name = m.name and s.ordinal = m.ordinal
where (s.name is not null and (m.name is null or upper(nvl(m.value, '~')) <> upper(nvl(s.value, '~'))))
or (s.name is null and (m.isdefault = 'FALSE' or m.ismodified <> 'FALSE'))
order by 1, 2;
前の章の再起動の前(鍋に2つ、レシピ帳に1つ書いた状態)に、CDB$ROOT で流した結果です。
NAME ORD MEMORY_NOW AFTER_RESTART NOTE
-------------------------- --- -------------- -------------- ----------------------------------------
open_cursors 1 400 300 differs: changes at restart
optimizer_index_caching 1 10 memory only: lost at restart
optimizer_index_cost_adj 1 100 50 differs: changes at restart
3 rows selected.
再起動で起きることが、3行で全部出ました。
- open_cursors:いま 400、再起動したら 300
- optimizer_index_caching:いま 10、SPFILE に書いていないので再起動したら消える
- optimizer_index_cost_adj:いま 100、再起動したら 50
再起動のあとに流すと、no rows selected。鍋とレシピ帳が一致した、という意味です。
大将「明日の仕込みの前に、鍋とレシピ帳を見比べる。……なるほど、味見ですね」
おうどん「そう。0 行なら、明日も今日と同じ味」
大将「では毎朝、味見してから仕込みます!」
……それは仕込みのあと。順番が逆です。
読み方:
| NOTE | 意味 | 次にやること |
|---|---|---|
differs: changes at restart | 再起動すると値が変わる | 意図した変更か確認。いまの値を残したいなら BOTH で設定し直す |
memory only: lost at restart | いまの値は SPFILE にない。再起動で既定値に戻る | 残したいなら BOTH。一時的な変更なら、そのままで計画どおり |
spfile only | SPFILE にあって、いまの値の行にない | 今回の検証では出ていない。並びの数(ORDINAL)が違うリストのパラメータなどを想定した行 |
新人さん「3行出ました! 全部、BOTH でいまの値に合わせます!」
おうどん「待って。optimizer_index_cost_adj の 50 は、わざとレシピ帳に書いた明日の予定かもしれない」
……差分は「直すものの一覧」ではなく「明日起きることの一覧」。直すかどうかは、書いた人の意図を確かめてからです。
この SQL で、わざと手を入れた3か所
差分 SQL、素直に書くと外れます。今回の検証で、3回外れました。
1つ目:大文字・小文字。
最初は m.value <> s.value と素直に比べていました。そのときの結果がこれです。
select nvl(m.name, s.name) name, nvl(m.ordinal, s.ordinal) ord, m.value mem_value, s.value sp_value,
case when s.name is null then 'memory only' when m.name is null then 'spfile only'
when m.value = s.value then 'same' else 'differs' end note
from (select name, ordinal, value, isdefault from v$system_parameter2 where con_id = 0) m
full outer join (select name, ordinal, value from v$spparameter where isspecified = 'TRUE' and sid in ('*', sys_context('USERENV','INSTANCE_NAME'))) s
on s.name = m.name and s.ordinal = m.ordinal
where (s.name is not null and (m.name is null or nvl(m.value,'~') <> nvl(s.value,'~')))
or (s.name is null and m.isdefault = 'FALSE')
order by 1, 2;
NAME ORD MEM_VALUE SP_VALUE NOTE
------------------------------ --- ------------------------------ ------------------------------ --------------------------------
audit_file_dest 1 C:\APP\<USER>\ADMIN\CDB19\ADUMP C:\app\<user>\admin\cdb19\adump differs
audit_trail 1 DB db differs
control_files 1 C:\APP\<USER>\ORADATA\CDB19\CON C:\app\<user>\oradata\CDB19\con differs
TROL01.CTL trol01.ctl
control_files 2 C:\APP\<USER>\ORADATA\CDB19\CON C:\app\<user>\oradata\CDB19\con differs
TROL02.CTL trol02.ctl
diagnostic_dest 1 C:\APP\<USER> C:\app\<user> differs
enable_pluggable_database 1 TRUE true differs
6 rows selected.
(今回の検証環境で、何も変更していない状態で実行した結果です。パスの一部を伏せています。伏せた文字数のぶん、列の位置が少しずれています)
何も変えていないのに、6行も「違う」。
よく見ると、メモリ側は DB、SPFILE 側は db。パスも、メモリ側は全部大文字です。中身は同じなのに、書き方だけが違う。
新人さん「6件も違いがあります! 全部直します!」
おうどん「待って、それ全部、大文字か小文字かの違い」
……6件の差分が、全員シロ。誤報で現場が走り出すのが、一番疲れます。
そこで完成版では、upper() で大文字にそろえてから比べています。
ただし、大文字・小文字の違いに意味がある値(大文字・小文字を区別する OS のパスなど)では、本当の違いを見落とす可能性があります。その種類のパラメータを変えたときは、upper() を外した結果も見てください。
2つ目:ISDEFAULT は、ALTER SYSTEM で変えても TRUE のまま。
前の章の表をもう一度見てください。SCOPE = MEMORY で 10 にした optimizer_index_caching は、ISDEFAULT が TRUE のままでした。
V$SYSTEM_PARAMETER2 のリファレンスでは、ISDEFAULT は「既定値のままか(TRUE)、パラメータファイルで指定されたか(FALSE)」と説明されています。ALTER SYSTEM で変えたかどうかは、ISMODIFIED のほうの役目です。
最初の版は ISDEFAULT だけを見ていたので、鍋だけの変更を見逃していました。完成版は ISMODIFIED も見ています。
大将「既定値ですか? はい、出汁は既定の味です(しょうゆ大さじ10入り)」
おうどん「入ってる入ってる」
……自己申告の「いつもどおり」ほど、信用できないものはない。
ただし ISMODIFIED は、元の値に戻しても再起動まで残ります。前の章で optimizer_index_caching を 0 に戻したあとに差分 SQL を流すと、こうなりました。
NAME ORD MEMORY_NOW AFTER_RESTART NOTE
-------------------------- --- -------------- -------------- ----------------------------------------
optimizer_index_caching 1 0 memory only: lost at restart
1 row selected.
(今回の検証環境で、BOTH で戻して RESET したあとに実行した結果です)
値は既定値の 0 なので、再起動しても実害はありません。「一度さわった跡」として出ている行です。MEMORY_NOW が既定値と同じなら、無視して大丈夫です。
3つ目:リストのパラメータと、単位の表示。
CONTROL_FILES(制御ファイルの場所)のように値が複数あるパラメータは、V$SYSTEM_PARAMETER だと1行にカンマでつながって出ます。V$SYSTEM_PARAMETER2 なら1つの値が1行になり、ORDINAL(何番目か)が付きます。V$SPPARAMETER も ORDINAL を持っているので、名前と ORDINAL で結べば、1個ずつ比べられます。
単位の表示にも注意です。SPFILE の中身をこう見ると。
SID NAME ORDINAL VALUE DISPLAY_VALUE
------ ------------------------------ ------- ---------------------------------------- --------------
* pga_aggregate_target 1 536870912 512M
* sga_target 1 2147483648 2G
(今回の検証環境で、V$SPPARAMETER の ISSPECIFIED が TRUE の行を全部出した結果から、2行だけ抜き出したものです)
DISPLAY_VALUE は 512M、VALUE はバイト数の 536870912。V$SPPARAMETER のリファレンスでも、DISPLAY_VALUE は人が読みやすい形(262144 なら 256K)と説明されています。
比べるなら、どちらも VALUE 同士です。片方だけ DISPLAY_VALUE にすると、512M と 536870912 が「違う」と出ます。
新人さん「512M と 536870912、どっちが正しいんですか」
おうどん「どっちも正しい。うどん1玉と、うどん200グラムの関係」
……言い方が違うだけで、お腹のふくれ方は同じです。
変更作業の手順:確認用と変更用を分ける
差分 SQL ができたら、変更作業はこの順番で組みます。
ここに書くコマンドのうち、CREATE PFILE 以外は今回の検証で実際に流したものと同じ形です(CREATE PFILE の扱いは下で説明します)。
1. 変更の前(確認用)
- SPFILE で起動しているか:
show parameter spfileで値が出れば SPFILE 起動。空なら PFILE 起動で、SCOPE = MEMORYしか使えません - 差分 SQL が 0 行か:0 行でなければ、先に「誰の、いつの変更か」を確かめる。人の時限装置の上に、自分の変更を重ねない
- 対象パラメータの
V$SYSTEM_PARAMETERの値と、V$SPPARAMETERのVALUE・ISSPECIFIEDを記録する(戻し方が決まる) - SPFILE の控えを取る
控えは、管理者ガイドでは CREATE PFILE で SPFILE をテキストに書き出す方法が紹介されています(FROM MEMORY で、いまのメモリの値を書き出すこともできます)。RMAN(Oracle のバックアップツール)でバックアップしている環境では、データベースのバックアップのときに SPFILE も自動で含まれる、とも書かれています。
ここで1つ、今回の検証でつまずいたことがあります。CREATE PFILE = '<作業用フォルダのパス>' FROM SPFILE; を流したら、こうなりました。
ORA-09210: sftopn: error opening file
(今回の検証環境で、ユーザーの一時フォルダの下を指定したときの出力から1行だけ抜き出したものです。続く2行は OS のエラーで、今回の出力では文字化けしていましたが、(OS 5) とあり、Windows のアクセス拒否のエラーでした)
ファイルを書いたのは、SQL*Plus を動かしている人ではなく、DB の側だったようです。今回の環境では、Oracle のサービスが自分のアカウントで動いていて、そのアカウントがログオン中のユーザーの一時フォルダに書けなかった、と考えると辻褄が合います(サービスのアカウントの権限までは調べていないので、ここは推測です)。
控えを書き出す場所は、DB サーバーのプロセスが書ける場所を選びましょう。
新人さん「レシピ帳のコピー、自分の机に置こうとしたら、大将に入れないって言われました」
おうどん「大将は厨房から出られないからね」
……コピーを取りに行くのは、大将本人。置き場所は、大将の手が届く棚にしましょう。
2. 変更(変更用)
■ 対象:open_cursors(CDB$ROOT / インスタンス全体)
■ 種類:動的(ISSYS_MODIFIABLE = IMMEDIATE)、SPFILE 起動
■ 変更前:V$SYSTEM_PARAMETER = 300、V$SPPARAMETER = 300(ISSPECIFIED = TRUE)、差分 SQL 0 行
■ 変更:alter system set open_cursors = 400 comment = '変更の理由と日付' scope = both;
■ 反映:すぐ(実行中のセッションにも)。再起動後も 400
■ 変更後の確認:V$SYSTEM_PARAMETER と V$SPPARAMETER がどちらも 400、差分 SQL が 0 行
■ 戻し方:alter system set open_cursors = 300 scope = both;
(変更前に ISSPECIFIED = FALSE だったパラメータなら、
alter system reset <名前> scope = spfile; と、元の値を scope = memory で設定)
(作業メモのひな形です。この中の scope = both で 400 にする行と、その戻し方の行は、今回は実行していません。BOTH での変更と RESET での戻し方は、前の章の optimizer_index_caching で実行して確かめています)
COMMENT を付けておくと、V$SPPARAMETER の UPDATE_COMMENT に残ります。3か月後の誰かが、レシピ帳の付箋を読めるようにしておきましょう。
3. 変更の後(確認用)
- 差分 SQL が 0 行(
MEMORYで一時的に変えたなら、その行だけが出ること) SCOPE = SPFILEで変えたなら、差分 SQL にdiffers: changes at restartとして出ることを確かめ、次の再起動の作業計画に書く
新人さん「変更前に確認して、変更して、変更後に確認。3回も見るんですか」
おうどん「再起動のあとに『え?』って言うのが、4回目にならないようにね」
……「え?」の回数を減らすのが、運用という仕事です。
ここから先は上級者向け。読み飛ばしてもOKです。
RESET の SCOPE ごとの動き:MEMORY は「起動したときの値」に戻る
ALTER SYSTEM RESET にも SCOPE があります。
ここは、今回の検証でちょっと意外だった結果から。
OPEN_CURSORS(SPFILE には 300)を SCOPE = MEMORY で 400 にして、ALTER SYSTEM RESET open_cursors SCOPE = MEMORY を流しました。
OPEN_CURSORS の既定値は 50 です。では、RESET したあとの値は。
300 でした。50 ではありません。
新人さん「RESET って、工場出荷の状態に戻すボタンじゃないんですか」
おうどん「スマホの初期化と同じ気持ちで押すと、ちょっと違う結果になるね」
……リセットという言葉に、人はつい夢を見すぎます。
管理者ガイドの「初期化パラメータの値の消去」には、非 CDB と CDB$ROOT での動きがこう書かれています。
| RESET の SCOPE | 動き |
|---|---|
SPFILE | SPFILE から消す。次の起動で既定値が効く |
MEMORY | 起動したときの値(startup value)がすぐ効く。SPFILE には残らず、再起動で失われる |
BOTH | SPFILE から消し、既定値がすぐ効く。再起動しても既定値 |
今回の 300 は、起動したときに SPFILE から読んだ値、と考えると説明どおりです。
同じガイドには、SCOPE = BOTH で RESET したあとは、SCOPE = MEMORY の RESET が常に既定値に戻すようになる、という注意も書かれています(この組み合わせは今回は試していません)。
大将「RESET ですか。では、今朝レシピ帳を読んだときの味に戻します」
おうどん「工場出荷の味じゃなくて?」
大将「工場出荷の味に戻すなら、BOTH と言ってください」
……「元に戻して」の「元」が、いつの時点のことか。人間の会話でも、だいたいここで揉めます。
PDB のレシピ帳は別冊になっている
CDB(コンテナ・データベース。親)と PDB(プラガブル・データベース。親の中にある子のデータベース)の構成では、PDB の中で ALTER SYSTEM を流すと、PDB 用の値として保存されます。
ALTER SYSTEM のリファレンスでは、PDB の中で変えた値はその PDB だけに効き、PDB で明示的に設定していないパラメータは CDB$ROOT の値を引き継ぐ、と説明されています。
PDB19 の中で、OPEN_CURSORS を SCOPE = SPFILE で 350 にしてみました(このとき CDB$ROOT のメモリは、前の章の実験で 400 でした)。
alter session set container = pdb19;
alter system set open_cursors = 350 scope = spfile;
select name, value, isspecified from v$spparameter where name = 'open_cursors';
select con_id, name, value from v$system_parameter where name = 'open_cursors';
alter session set container = cdb$root;
select con_id, name, value, isspecified from v$spparameter where name = 'open_cursors';
Session altered.
System altered.
NAME VALUE ISSPEC
-------------------------- -------------- ------
open_cursors 350 TRUE
1 row selected.
CON_ID NAME VALUE
------ -------------------------- --------------
0 open_cursors 400
1 row selected.
Session altered.
CON_ID NAME VALUE ISSPEC
------ -------------------------- -------------- ------
1 open_cursors 300 TRUE
1 row selected.
(今回の検証環境で、2つ目の章の ALTER SYSTEM のあと、再起動の前に実行した結果です)
PDB の中から V$SPPARAMETER を見ると 350。CDB$ROOT から見ると 300。
同じビュー名なのに、どこから見るかで、見えるレシピ帳が違います。PDB の中の V$SPPARAMETER は、その PDB の別冊を見せていました。
再起動のあと、CDB$ROOT から V$SYSTEM_PARAMETER を見ると、こう出ました。
select p.con_id, nvl(c.name, '(all)') pdb_name, p.name, p.value
from v$system_parameter p left join v$containers c on c.con_id = p.con_id
where p.name = 'open_cursors' order by p.con_id;
CON_ID PDB_NAME NAME VALUE
------ ---------- -------------------------- --------------
0 (all) open_cursors 300
3 PDB19 open_cursors 350
2 rows selected.
(今回の検証環境で、再起動のあと CDB$ROOT で実行した結果です)
CDB$ROOT から見ると、PDB 独自の値は CON_ID 付きの別の行として出ます。CON_ID 0 は CDB 全体の行です。
新人さん「1階の本店のレシピ帳を見たら、2階の支店の味が書いてなかったんです」
おうどん「2階は2階で、別冊を持ってるからね」
……本店の帳簿だけ見て「全店この味です」と報告すると、2階の常連さんに怒られます。
ここで、差分 SQL の注意です。
差分 SQL は CDB$ROOT(または非 CDB)で流してください。今回、同じ SQL を PDB19 の中で流したら、CDB の SPFILE に書いてある audit_trail や control_files など 16 行が memory only: lost at restart と出て、本当の差(open_cursors の 400 と 350)は 1 行だけでした。PDB の中の V$SPPARAMETER は別冊だけ、いまの値は CDB から引き継いだものも含む、という見え方の違いのせいです。
PDB ごとの値は、PDB の中で V$SPPARAMETER の ISSPECIFIED が TRUE の行を見るのが確実です。
大将(2階)「別冊どおりに作りました! 別冊にないことは本店のレシピ帳どおりです!」
おうどん「それを1冊ずつ確かめるのは、こっちの仕事だね」
……ビルが高くなるほど、レシピ帳の数も増える。
(PDB の閉じ開きだけでの反映は試していません。リファレンスでは、PDB で SCOPE = SPFILE にした値は、PDB を閉じて開き直すか、CDB を再起動したときに効く、と書かれています。今回確かめたのは CDB の再起動のほうです)
誰がいつレシピ帳に書いたかの手がかり
3か月前の時限式の書き込み。犯人捜しではなく、意図を確かめるための手がかりが3つあります。
1. UPDATE_COMMENT。COMMENT = '...' を付けて変えていれば、V$SPPARAMETER の UPDATE_COMMENT に残ります。ALTER SYSTEM のリファレンスでは、SPFILE も指定したときに、そのパラメータの最新の変更を示すものとしてパラメータファイルに残る、と説明されています。付けていなければ空です。
2. アラートログ。
今回の検証環境では、ALTER SYSTEM の文がアラートログ(DB のできごとを書き残すログ)にそのまま記録されていました。SQL からは V$DIAG_ALERT_EXT で読めます。
select to_char(originating_timestamp, 'HH24:MI:SS') t, message_text
from v$diag_alert_ext
where originating_timestamp > systimestamp - interval '15' minute
and message_text like 'ALTER SYSTEM%'
order by originating_timestamp;
T MESSAGE_TEXT
-------- ------------------------------------------------------------------------------------------
08:12:55 ALTER SYSTEM SET open_cursors=400 SCOPE=MEMORY;
08:12:55 ALTER SYSTEM RESET open_cursors SCOPE=MEMORY;
08:12:55 ALTER SYSTEM SET open_cursors=400 SCOPE=MEMORY;
08:12:55 ALTER SYSTEM SET optimizer_index_caching=10 SCOPE=MEMORY;
08:12:55 ALTER SYSTEM SET optimizer_index_cost_adj=50 COMMENT='test 2026-10-14 reset after' SCOPE=S
PFILE;
08:12:55 ALTER SYSTEM SET open_cursors=350 SCOPE=SPFILE PDB='PDB19';
6 rows selected.
(今回の検証環境で、この記事の実験の直後に実行した結果です。col で列幅を指定したため、長い行が折り返しています)
SCOPE = MEMORY の変更も、PDB の中での変更(PDB='PDB19')も、ちゃんと残っていました。SPFILE には痕跡が残らない鍋だけの変更も、ここなら追えます。
ただし、アラートログは古いものが整理されていることがありますし、どのメッセージがどの形で残るかは、今回の 19.3 で確かめた範囲です。3か月前の分が残っているかは、環境しだいです。
大将「厨房日誌には、全部書いてあります!」
おうどん「日誌は付けてたのに、レシピ帳の付箋は忘れたんだね」
……日誌が分厚いと、それはそれで探すのが大変です。
3. PFILE で起動していないか。V$SPPARAMETER のリファレンスによると、SPFILE を使わずに起動したインスタンスでは、全部の行の ISSPECIFIED が FALSE になります。差分 SQL も、この場合は SPFILE 側が空になります。
管理者ガイドによると、PFILE 句なしの STARTUP は、OS ごとの既定の場所で spfile<SID>.ora、spfile.ora の順に探し、どちらもなければ init<SID>.ora(PFILE)を探します。「SPFILE を直したのに効かない」ときは、そもそも SPFILE で起動していない可能性も見ましょう。
最後に1つ。SPFILE はバイナリのファイルですが、テキストエディタで開くと中身が読めてしまいます。管理者ガイドには、手で編集しないこと、壊れて起動できなくなる、とはっきり書かれています。直すなら ALTER SYSTEM か、PFILE に書き出して直してから CREATE SPFILE で作り直す方法です。
新人さん「レシピ帳、読めたので、そのまま鉛筆で直しておきました」
大将「……明日、私は起きられないかもしれません」
……大将の目覚ましを、鉛筆1本で壊さないでください。
🍜 まとめ:再起動の前に、鍋とレシピ帳を見比べる
冒頭の新人さんの2つの「え?」は、こうでした。
- 400 にしたのに 300 に戻った →
SCOPE = MEMORYで変えていた - 誰も触っていないのに変わった → 前に誰かが
SCOPE = SPFILEで書いていた
次からは、こう言えます。
新人さん「日曜のメンテの前に、差分 SQL を流しました。0 行です」
おうどん「完璧。じゃあ月曜の朝も、今日と同じ味だね」
大将「レシピ帳どおりに作ります!」
おうどん「うん。今回は、レシピ帳が正しいからね」
……大将の忠実さは、レシピ帳が正しいときだけ、最高の長所になります。
SCOPE は「どのノートに書くか」。変えたら、いまの値と再起動後の値を両方見て、再起動の前に食い違いをゼロにする。
新人さん「鍋とレシピ帳、両方見る。覚えました。……ところで、うちの店のレシピ帳って何冊ありました?」
おうどん「CDB に1冊。あとは PDB ごとの別冊」
……覚えることが1個増えたところで、今日は閉店です。
まずは、次の計画停止の前に、差分 SQL を1回だけ流してみてください。
0 行だったら、安心してきつねうどんでもどうぞ🍜
参考資料
- Oracle Database 19c SQL Language Reference:ALTER SYSTEM — SCOPE の MEMORY・SPFILE・BOTH の意味と既定値、PFILE 起動では MEMORY だけであること、静的パラメータは SPFILE を指定すること、COMMENT 句、RESET 句、PDB の中で変えたときの反映と CDB$ROOT からの引き継ぎ
- Oracle Database 19c Administrator’s Guide:Creating and Configuring an Oracle Database(2.7 サーバー・パラメータ・ファイルの管理) — SCOPE ごとの効果(SPFILE は動的・静的どちらも次の起動から)、PFILE 起動で SPFILE・BOTH を指定するとエラーになること、RESET の SCOPE ごとの動き(MEMORY は起動時の値)、起動時の SPFILE・PFILE の探し方、SPFILE を手で編集しないこと、CREATE PFILE FROM SPFILE / FROM MEMORY、RMAN による SPFILE のバックアップ、SHOW PARAMETERS・SHOW SPPARAMETERS と各ビューの違い
- Oracle Database 19c Reference:V$SPPARAMETER — SPFILE の中身を表示すること、ISSPECIFIED・ORDINAL・DISPLAY_VALUE・UPDATE_COMMENT の意味、SPFILE を使わずに起動すると ISSPECIFIED がすべて FALSE になること
- Oracle Database 19c Reference:V$SYSTEM_PARAMETER2 — インスタンスで有効な値をリストの値ごとに1行で表示すること、ISDEFAULT・ISMODIFIED・CON_ID の意味
確認日:2026-10-14。

コメント