こんにちは、おうどんです🍜
新人さん「クラウドの OCPU を 2 から 4 に増やしてもらいました!」
おうどん「よかったね。じゃあ Oracle の CPU_COUNT はいくつ?」
新人さん「……4 ですよね?」
おうどん「8 かもしれないし、4 かもしれないし、誰かが固定した 2 かもしれない」
新人さん「……え」
(会話は説明用の架空の場面です)
OCPU、vCPU、コア、スレッド、OS の CPU 数、CPU_COUNT。
全部「CPU の数」っぽい顔をしているのに、数えているものが少しずつ違います。
これ、おうどんも「結局どれがどれ?」と迷ったことがある疑問です。
うどん屋の厨房でたとえると、こうなります。
- OCPU:大家さんと契約した「コンロの口数」
- コア:本物のコンロ
- スレッド(vCPU):1つのコンロに鍋を2つ並べて、交互に火を当てる置き方
- CPU_COUNT:大将(Oracle)が「うちは鍋が何個ある店か」と思って、段取りを決めるときに使う数
大将(Oracle)「鍋は4個と聞いてます! 4個ぶんの段取りで茹でます!」
……鍋は4個。でも、コンロは2口しかない。この話は、あとで効いてきます。
最初に、初心者向けのチェックリストです。
- Oracle がいま使っている CPU の数は
V$PARAMETERのcpu_count、OS から見えている数はV$OSSTATのNUM_CPUSで確かめる CPU_COUNTを小さくしただけでは、CPU の使用量に上限はかからないことがある(リソース・プランが有効なときだけ上限になる)- クラウドの OCPU の数を、そのまま CPU_COUNT の数だと思わない(換算はサービスと CPU の種類で違う)
この記事の検証は、今回の検証環境(Oracle Database 19c 19.3、シングルインスタンス・CDB 構成)で、SQL*Plus から SYSDBA で実行したものです。CPU_COUNT を一時的に変えて負荷をかける実験もしています(最後に元に戻しました)。
OCPU から CPU_COUNT まで、数える場所が4段ある
いきなり結論です。CPU の数は、クラウドの契約 → OS → Oracle のインスタンス → PDB の4段で、それぞれ別に数えられています。
| 段 | 何を数えている | どこで見る |
|---|---|---|
| クラウドの割り当て | OCPU(Oracle Cloud の単位)、vCPU | クラウドのコンソール・シェイプの説明 |
| OS | 論理 CPU(スレッド)、コア、ソケット | OS のコマンド、V$OSSTAT |
| Oracle のインスタンス | Oracle が使ってよい CPU の数(CPU_COUNT) | V$PARAMETER の cpu_count |
| PDB | その PDB の CPU_COUNT | PDB の中の V$PARAMETER |
用語を少しだけ。
- OCPU(Oracle CPU):Oracle Cloud Infrastructure(OCI)の CPU の単位。物理的なコアを表します
- vCPU(仮想 CPU):物理コアの「実行スレッド」1本ぶん
- ハイパースレッディング:1つのコアで2本のスレッドを同時に動かす仕組み。OS からは CPU が2個に見えます
- CDB・PDB:CDB(コンテナ・データベース)は親、PDB(プラガブル・データベース)はその中にある子のデータベース
新人さん「4段もあるなら、どれか1つ見れば全部わかる魔法のビューがありますよね?」
おうどん「あったら、この記事は3行で終わってる」
……3行で終わる記事を書きたい。できれば寝ている間に。
OCPU と vCPU の換算は、CPU の種類で変わる
OCI のコンピュート・シェイプのドキュメントでは、OCPU は物理 CPU コアを表し、vCPU は物理コアの実行スレッド1本を表す、と説明されています。そのうえで、換算はこう書かれています。
| CPU の種類(OCI Compute) | 1 OCPU は |
|---|---|
| x86(AMD・Intel) | 2 vCPU |
| Arm A1 | 1コア = 1 vCPU |
| Arm A2・A4 | 2コア = 2 vCPU |
同じ「1 OCPU」でも、x86 は「1コアに2スレッド」、Arm A1 は「1コア1スレッド」、A2・A4 は「2コア」です。
大家さん「1口ぶんの契約です。ただし、店によっては鍋が2個置けます」
おうどん「契約書に、鍋の数も書いておいてほしい」
……コンロの口数と鍋の数が、契約の単位によってずれる。不動産屋さんで聞いたら、たぶん怒られる説明です。
ここで大事なのは、OCPU の数から CPU_COUNT を暗算しないことです。上の表は OCI のコンピュート・シェイプの話で、Exadata やほかのクラウド・サービスの単位は、それぞれのサービスの資料で確かめる必要があります。「OCPU × 2 が CPU_COUNT」と覚えてしまうと、Arm のシェイプで外れます。
確実なのは、OS に上がってきた数を、DB の中から見ることです。
OS から見えている数は V$OSSTAT で見る
V$OSSTAT は、OS から集めた統計を1行1項目で見せるビューです。V$OSSTAT のリファレンスによると、CPU の数まわりの項目はこう分かれています。
NUM_CPUS:使える CPU(プロセッサ)の数NUM_CPU_CORES:CPU コアの数NUM_CPU_SOCKETS:物理的な CPU ソケット(チップ)の数NUM_LCPUS・NUM_VCPUS:論理 CPU の数、仮想 CPU の数
今回の検証環境で流してみます。
select stat_name, to_char(value) value, comments from v$osstat where stat_name like 'NUM%' order by stat_name;
STAT_NAME VALUE COMMENTS
------------------ -------------- ----------------------------------------
NUM_CPUS 4 Number of active CPUs
NUM_CPU_CORES 2 Number of CPU cores
NUM_CPU_SOCKETS 1 Number of physical CPU sockets
3 rows selected.
(今回の検証環境で実行した結果です。列幅は SQL*Plus の col で調整しています。VALUE は数値の列なので、to_char で文字にしてから幅をそろえています)
CPU は4個、コアは2個、ソケットは1個。
OS の側(PowerShell)でも見てみました。
Get-CimInstance Win32_Processor | Select-Object Name,NumberOfCores,NumberOfLogicalProcessors | Format-List
Name : Intel(R) Core(TM) i7-7660U CPU @ 2.50GHz
NumberOfCores : 2
NumberOfLogicalProcessors : 4
(今回の検証環境の Windows PowerShell 5.1 で実行した結果です。前後の空行は省いています)
2コア4スレッド。NUM_CPUS の 4 は、コアではなくスレッドの数でした。Python の os.cpu_count() も 4 を返しました。
新人さん「NUM_LCPUS と NUM_VCPUS が出てきません! 壊れてますか?」
おうどん「壊れてない。リファレンスに、全部の OS で必ず出るのは NUM_CPUS と RSRC_MGR_CPU_WAIT_TIME だけ、って書いてある」
……出ない行があっても慌てない。今回の Windows では、NUM_ で始まる行は3つだけでした。
大将「コンロは2口、鍋は4個。合計で4人前を同時に茹でられます!」
おうどん「同時に茹でられるのは本当。ただ、2個ずつ同じ火を取り合ってるけどね」
……鍋が4個あることと、4倍速く茹で上がることは別。これも、あとで数字で見ます。
CPU_COUNT の既定値は 0。でも 0 とは表示されない
CPU_COUNT のリファレンス(19c)には、こう書かれています。
- CPU_COUNT は、Oracle が使える CPU の数(マルチスレッドの CPU では CPU スレッドの数)を指定する
- オプティマイザ(SQL の実行方法を決める係)、パラレル・クエリ、リソース・マネージャが、自分の設定を決めるときにこの値を使う
- 既定値は 0。0 のときは OS が報告する CPU の数を見張り続けて、いまの数を使う(CPU の数が動的に変わっても追いかける)
- 0 以外を設定すると、実際の数ではなくその値を使う(動的な追従は止まる)
- ALTER SYSTEM で変更でき、PDB の中でも変更できる
では、今回の検証環境の値を見ます。
set lines 200 pages 200 tab off feedback on
col name for a26
col value for a20
col display_value for a14
col isdefault for a9
col ismodified for a10
col issys_modifiable for a16
col ispdb_modifiable for a16
select name, value, isdefault, ismodified, issys_modifiable, ispdb_modifiable from v$parameter
where name in ('cpu_count','cpu_min_count','parallel_threads_per_cpu','resource_manager_plan','parallel_max_servers','sessions','processes') order by name;
NAME VALUE ISDEFAULT ISMODIFIED ISSYS_MODIFIABLE ISPDB_MODIFIABLE
-------------------------- -------------------- --------- ---------- ---------------- ----------------
cpu_count 4 TRUE FALSE IMMEDIATE TRUE
cpu_min_count 4 TRUE FALSE IMMEDIATE TRUE
parallel_max_servers 80 TRUE FALSE IMMEDIATE TRUE
parallel_threads_per_cpu 1 TRUE FALSE IMMEDIATE FALSE
processes 320 FALSE FALSE FALSE FALSE
resource_manager_plan SCHEDULER[0x4D1E]:DE TRUE FALSE IMMEDIATE TRUE
FAULT_MAINTENANCE_PL
AN
sessions 504 TRUE FALSE IMMEDIATE TRUE
7 rows selected.
(今回の検証環境の CDB$ROOT で実行した結果です。同じスクリプトで先に流したバージョン確認の SELECT と、ほかの列の col 指定は省いています。resource_manager_plan は列幅が足りずに折り返しています)
cpu_count は ISDEFAULT = TRUE(既定値のまま)なのに、VALUE は 0 ではなく 4。
既定値の 0 は「OS に合わせて」という意味の合図で、表示されるのは、合わせた結果の数なんです。SPFILE(起動時に読む設定ファイル)の中身を見る V$SPPARAMETER でも、cpu_count は ISSPECIFIED = FALSE(書かれていない)でした。
新人さん「既定値は 0 なのに 4 と出てる。ということは、誰かが 4 に設定したんですね!」
おうどん「ISDEFAULT が TRUE だから、誰も設定してない。0 は『OS に聞いて』のサイン」
……既定値という名の、ただの「おまかせ」。メニューの「時価」みたいなものです。
ISSYS_MODIFIABLE = IMMEDIATE なので、動いたまま変えられます(再起動の要否の見分け方は、Oracleの初期化パラメータ変更で再起動が必要か調べる方法にまとめました)。
それと、表の中に1行、気になる値が混ざっていました。resource_manager_plan の SCHEDULER[0x4D1E]:DEFAULT_MAINTENANCE_PLAN。これが、この記事の後半の主役です。
大将「プラン? 私は何も頼んでいません」
おうどん「大将が頼んでなくても、土曜の朝は店長が来るんだよ」
……誰も呼んでいないのに、毎週来る店長。この人の正体は、2つ先の章で。
OS の数より大きくは設定できない
リファレンスには、実際の CPU の数より大きい値を設定するとエラーになる、とも書かれています。8 にしてみました。
alter system set cpu_count = 8 scope = memory;
alter system set cpu_count = 8 scope = memory
*
ERROR at line 1:
ORA-02097: parameter cannot be modified because specified value is invalid
ORA-02097: parameter cannot be modified because specified value is invalid
(今回の検証環境の CDB$ROOT で実行した結果です。ORA-02097 の行が2回出ているのも、出力のままです。直後の show parameter cpu_count は 4 のままでした)
新人さん「OCPU を増やす前に、先に CPU_COUNT を 8 にしておきます!」
おうどん「先にやると、こうなる。鍋が届く前に、8人前の段取りは組めない」
……届いていない鍋のぶんまで張り切る大将。気持ちだけ受け取っておきます。
ちなみに、パラメータファイル(PFILE・SPFILE)に実際より大きい値が書いてあった場合は、エラーではなく実際の数で頭打ちになる、とリファレンスにあります(今回は試していません)。
🔰 ここまで読めば今日から困らない
- CPU の数は「クラウドの割り当て → OS → インスタンス → PDB」の4段で別々に数える
- OS の数は
V$OSSTATのNUM_CPUS(ハイパースレッディングがあればスレッドの数) - Oracle が使う数は
V$PARAMETERのcpu_count。既定値 0 は「OS に合わせる」で、表示は合わせた結果の数 - OCPU から CPU_COUNT を暗算しない。DB の中で
NUM_CPUSとcpu_countを見る - OS の数より大きい CPU_COUNT は、動いたまま設定すると ORA-02097
ここから先は中級者向け。CPU_COUNT を小さくしたら、本当に CPU の使用量が減るのかを実験します。
CPU_COUNT を 1 にしたら、遅くなった(土曜の朝だから)
CPU_COUNT のリファレンスには、もう1つ大事なことが書いてあります。RESOURCE_MANAGER_PLAN が設定されているときは、データベースの CPU 使用量が CPU_COUNT のスレッド数に制限される。これをインスタンス・ケージング(インスタンスを檻に入れる、という意味)と呼びます。
逆に言うと、リソース・マネージャ(CPU などをセッションのグループに配分する機能)が動いていなければ、CPU_COUNT は「段取りに使う数」でしかありません。管理者ガイドのリソース・マネージャの章にも、RESOURCE_MANAGER_PLAN でプランを指定しなければリソース・マネージャは有効にならない、と書かれています。
店長(リソース・マネージャ)「鍋は1個までね、と張り紙をしても、見張る人がいなければ誰も守りません」
おうどん「張り紙だけで守られる職場、ちょっと憧れる」
……張り紙を書いたことで満足して帰る人、いますよね。CPU_COUNT だけ変えた人がそれです。
実験の段取り
PDB19 にテスト用のユーザーを作り、同じ計算をする PL/SQL を、4つのセッションから同時に流しました(Python から SQL*Plus を4つ同時に起動)。中身は、ひたすら足し算をするだけのループです。
set serveroutput on feedback off
declare
t0 number := dbms_utility.get_time;
c0 number := dbms_utility.get_cpu_time;
x number := 0;
begin
for i in 1 .. 15000000 loop
x := x + mod(i, 7);
end loop;
dbms_output.put_line('elapsed=' || to_char((dbms_utility.get_time - t0)/100, 'FM990.00')
|| ' cpu=' || to_char((dbms_utility.get_cpu_time - c0)/100, 'FM990.00'));
end;
/
exit
elapsed は経過時間、cpu はそのセッションが CPU を使った時間(どちらも秒)です。DBMS_UTILITY.GET_TIME と GET_CPU_TIME が 100 分の 1 秒単位なので、100 で割っています。
まず、何も変えていない状態(CPU_COUNT は既定の 4)。
elapsed=7.17 cpu=6.94
elapsed=7.22 cpu=7.05
elapsed=7.18 cpu=6.95
elapsed=7.19 cpu=7.00
(今回の検証環境で、4セッションを同時に流した結果です。1行が1セッションです)
4セッションとも、だいたい7秒。並んで走っています。
次に、CDB$ROOT で CPU_COUNT を 1 にしてから、同じ4セッションを流しました。変える前後の状態を見る SQL も一緒に流しています。
alter system set cpu_count = 1 scope = memory;
set lines 200 pages 200 tab off feedback on
col name for a26
col value for a40
col plan for a30
select name, value, isdefault, ismodified from v$parameter where name in ('cpu_count','resource_manager_plan') order by name;
select con_id, name plan, cpu_managed, instance_caging from v$rsrc_plan order by con_id;
select stat_name, to_char(value) value from v$osstat where stat_name like 'NUM%' order by stat_name;
System altered.
NAME VALUE ISDEFAULT ISMODIFIED
-------------------------- ---------------------------------------- --------- ----------
cpu_count 1 TRUE SYSTEM_MOD
resource_manager_plan SCHEDULER[0x4D1E]:DEFAULT_MAINTENANCE_PL TRUE FALSE
AN
2 rows selected.
CON_ID PLAN CPU INS
---------- ------------------------------ --- ---
1 DEFAULT_MAINTENANCE_PLAN ON ON
2 INTERNAL_PLAN ON OFF
3 INTERNAL_PLAN ON OFF
3 rows selected.
STAT_NAME VALUE
---------------------------------------------------------------- ----------------------------------------
NUM_CPUS 4
NUM_CPU_CORES 2
NUM_CPU_SOCKETS 1
3 rows selected.
(今回の検証環境の CDB$ROOT で実行した結果です。v$rsrc_plan の列名は、列幅に合わせて CPU(CPU_MANAGED)と INS(INSTANCE_CAGING)に切れています)
INSTANCE_CAGING が、CDB$ROOT(CON_ID 1)の行で ON になりました。変える前は、同じ行が OFF でした。
そして、4セッションの結果。
elapsed=16.73 cpu=4.20
elapsed=16.94 cpu=4.37
elapsed=16.79 cpu=4.24
elapsed=16.95 cpu=4.46
(今回の検証環境で、CPU_COUNT = 1 にした直後に4セッションを同時に流した結果です)
7秒が、17秒になりました。
しかも、1セッションの CPU 時間は約4.3秒。4セッションぶん足すと約17秒で、経過時間とほぼ同じです。CPU 1個ぶんの時間を、4人で順番に使っていた、と読めます。
新人さん「CPU_COUNT を 1 にしたら、ちゃんと 1 個に絞られました! 張り紙、効くじゃないですか」
おうどん「今日が土曜だからね」
新人さん「……曜日、関係あります?」
……あるんです。それが、さっきの SCHEDULER[0x4D1E] の正体です。
店長が来ているのは、メンテナンス・ウィンドウの間
resource_manager_plan に入っていた SCHEDULER[0x4D1E]:DEFAULT_MAINTENANCE_PLAN は、スケジューラ(Oracle の中のジョブ管理)が自動で設定した値です。管理者ガイドのリソース・マネージャの章には、既定ではリソース・マネージャは有効ではないが、あらかじめ用意されたメンテナンス・ウィンドウ(統計情報の収集などの自動メンテナンス作業を流す時間帯)の間は DEFAULT_MAINTENANCE_PLAN で有効になる、と書かれています。
今回の検証環境のウィンドウを見てみると。
set lines 200 pages 200 tab off feedback on
col window_name for a16
col repeat_interval for a60
col duration for a14
col resource_plan for a26
select window_name, repeat_interval, to_char(duration) duration, resource_plan
from dba_scheduler_windows where window_name in ('SATURDAY_WINDOW','MONDAY_WINDOW') order by window_name;
WINDOW_NAME REPEAT_INTERVAL DURATION RESOURCE_PLAN
---------------- ------------------------------------------------------------ -------------- --------------------------
MONDAY_WINDOW freq=daily;byday=MON;byhour=22;byminute=0; bysecond=0 +000 04:00:00 DEFAULT_MAINTENANCE_PLAN
SATURDAY_WINDOW freq=daily;byday=SAT;byhour=6;byminute=0; bysecond=0 +000 20:00:00 DEFAULT_MAINTENANCE_PLAN
2 rows selected.
(今回の検証環境で実行した結果です)
月曜は夜22時から4時間。土曜は朝6時から20時間。
ほかの曜日も同じビューで見ると、今回の検証環境では火〜金が月曜と同じ夜22時から4時間、日曜が土曜と同じ朝6時から20時間でした。
今回の実験は、土曜の朝、SATURDAY_WINDOW が開いている時間に流していました(DBA_SCHEDULER_WINDOWS の ACTIVE が TRUE でした)。
店長「平日の昼は来ません。夜と週末だけ来て、張り紙を守らせます」
おうどん「ブラックなのか、ホワイトなのか、よくわからない勤務形態」
……ここで、怖い話を1つ。説明用の架空の場面です。
- 平日の昼に、誰かが「CPU は 2 個ぶんで十分」と CPU_COUNT = 2 を設定する
- 平日の昼はリソース・プランがないので、何も起きない。「影響なし」と報告される
- 夜22時や土曜6時にメンテナンス・ウィンドウが開いて、
DEFAULT_MAINTENANCE_PLANが有効になる - その時間だけ、インスタンス全体が CPU 2 個ぶんに絞られる
今回の検証で確かめたのは 3〜4 の部分(ウィンドウが開いている間に、CPU_COUNT を設定すると INSTANCE_CAGING が ON になり、遅くなる)です。平日の昼のプランがない時間帯での動き(2)は、今回は試しておらず、リファレンスと管理者ガイドの説明に基づきます。
新人さん「平日の昼にテストして、影響なしでした! 本番にも入れます!」
おうどん「店長がいない時間にテストして、店長がいる時間に本番、になってない?」
……張り紙が効くかどうかは、店長の出勤表しだい。
既定値のままなら、プランがあっても絞られない
もう1つ、表をよく見ると、CPU_COUNT を変える前(既定値のまま)は、DEFAULT_MAINTENANCE_PLAN が有効なのに INSTANCE_CAGING は OFF でした。
CON_ID PLAN CPU INS
---------- ------------------------------ --- ---
1 DEFAULT_MAINTENANCE_PLAN ON OFF
2 INTERNAL_PLAN ON OFF
3 INTERNAL_PLAN ON OFF
3 rows selected.
(今回の検証環境の CDB$ROOT で、CPU_COUNT を変える前に実行した結果のうち、v$rsrc_plan の部分です)
管理者ガイドの「インスタンス・ケージングを有効にする」手順も、CPU のディレクティブ(配分の指示)を持つプランを有効にすることと、CPU_COUNT を設定することの2つがセットになっています。今回の検証環境では、CPU_COUNT を明示的に設定したときに、はじめて ON になりました。
CPU_MANAGED は、変える前から ON です。リソース・マネージャはセッションのグループの間で CPU を配分しているけれど、インスタンス全体の上限(ケージ)はかかっていない、という状態です。
大将「店長は来ているけど、鍋の数は数えていない、と」
おうどん「店長、配膳の順番だけ仕切ってる」
……配膳係としては優秀。上限係としては、張り紙を待っている。
鍋が4個でも、コンロが2口なら4倍速くはない
さっきの結果を、もう一度見比べます。
| 状態 | 経過時間 | 1セッションの CPU 時間 |
|---|---|---|
| CPU_COUNT 既定(4)・4セッション同時 | 約7.2秒 | 約7.0秒 |
| CPU_COUNT = 1・4セッション同時 | 約16.8秒 | 約4.3秒 |
(上の2つの実行結果を、読みやすく表にまとめたものです)
同じ計算なのに、4つ同時に走ったときのほうが、1セッションの CPU 時間が長い。
今回の検証環境は2コア4スレッドなので、4セッションが同時に走ると、2つずつ同じコアを取り合います。CPU 時間は「CPU(スレッド)に乗っていた時間」なので、隣のスレッドとコアを分け合うと、同じ仕事でも長くなる、と考えると辻褄が合います(コアの中で何が起きているかまでは測っていません)。
なお、同じループを 3,000 万回にして1セッションだけで流したときは、elapsed=8.03 cpu=7.97 でした。1,500 万回ぶんに直すと約4秒で、CPU_COUNT = 1 のときの約4.3秒と近い値です。
新人さん「CPU が 4 個あるなら、4 倍速いと思ってました」
おうどん「鍋が 4 個でも、火は 2 口。4 人前を同時に入れたら、1 人前ずつは遅くなる」
……鍋を増やして満足する前に、コンロを数えよう。
ここで言いたいのは、CPU_COUNT や NUM_CPUS の数と、処理の速さは比例しないことです。スレッドとコアの違いは、性能を見積もるときに効いてきます。
PDB ごとの CPU_COUNT と、元に戻し方
CPU_COUNT は PDB の中でも変えられます。リファレンスには、CDB のレベルでリソース・マネージャが有効なら、PDB の CPU 使用量はその PDB 自身の CPU_COUNT に制限される、とあります。
CDB$ROOT の CPU_COUNT を元に戻してから、PDB19 の中だけ 1 にしてみました。
set lines 200 pages 200 tab off feedback on
col name for a26
col value for a40
col plan for a30
alter session set container = pdb19;
alter system set cpu_count = 1 scope = memory;
select con_id, name, value, isdefault, ismodified from v$parameter where name = 'cpu_count';
alter session set container = cdb$root;
select con_id, name, value from v$system_parameter where name = 'cpu_count' order by con_id;
select con_id, name plan, cpu_managed, instance_caging from v$rsrc_plan order by con_id;
Session altered.
System altered.
CON_ID NAME VALUE ISDEFAULT ISMODIFIED
---------- -------------------------- ---------------------------------------- --------- ----------
3 cpu_count 1 TRUE SYSTEM_MOD
1 row selected.
Session altered.
CON_ID NAME VALUE
---------- -------------------------- ----------------------------------------
0 cpu_count 4
3 cpu_count 1
2 rows selected.
CON_ID PLAN CPU INS
---------- ------------------------------ --- ---
1 DEFAULT_MAINTENANCE_PLAN ON OFF
2 INTERNAL_PLAN ON OFF
3 INTERNAL_PLAN ON ON
3 rows selected.
(今回の検証環境で実行した結果です)
今度は CDB$ROOT の行は OFF のまま、PDB19(CON_ID 3)の行だけが ON。CDB$ROOT から V$SYSTEM_PARAMETER を見ると、CDB 全体は 4、PDB19 だけ 1 という2行が出ています。
PDB19 で4セッションを流した結果です。
elapsed=22.09 cpu=6.00
elapsed=21.80 cpu=5.78
elapsed=20.89 cpu=5.54
elapsed=21.00 cpu=5.57
(今回の検証環境で、PDB19 の CPU_COUNT だけを 1 にして、PDB19 に4セッションを同時に流した結果です)
7秒が、約21秒。PDB19 だけが絞られました。
1セッションの CPU 時間が CDB$ROOT で絞ったとき(約4.3秒)より長い理由は、今回は調べていません。
2階の支店長「うちの支店は、鍋1個でやります」
1階の大将「1階は4個のままです!」
おうどん「同じビルなのに、階によって鍋の数が違う」
……2階のお客さんだけ、ちょっと待たされる店になりました。
新人さん「じゃあ、PDB の CPU_COUNT を足すと、ビル全体の CPU の数になるんですね!」
おうどん「ならない。PDB ごとの上限で、合計がぴったり合う決まりはない」
……管理者ガイドでも、インスタンスの上限の合計を実際の CPU の数より大きくする「オーバーサブスクライブ」というやり方が紹介されています(下の上級者向けの章で)。
元に戻す
今回は、どちらも SCOPE = MEMORY の RESET で戻しました。もともと SPFILE に cpu_count は書かれていなかったので、起動したときの値(既定)に戻れば元どおりです。
set lines 200 pages 200 tab off feedback on
alter system reset cpu_count scope = memory;
col name for a26
col value for a40
col plan for a30
select name, value, isdefault, ismodified from v$parameter where name in ('cpu_count','resource_manager_plan') order by name;
select con_id, name plan, cpu_managed, instance_caging from v$rsrc_plan order by con_id;
select stat_name, to_char(value) value from v$osstat where stat_name like 'NUM%' order by stat_name;
System altered.
NAME VALUE ISDEFAULT ISMODIFIED
-------------------------- ---------------------------------------- --------- ----------
cpu_count 4 TRUE FALSE
resource_manager_plan SCHEDULER[0x4D1E]:DEFAULT_MAINTENANCE_PL TRUE FALSE
AN
2 rows selected.
CON_ID PLAN CPU INS
---------- ------------------------------ --- ---
1 DEFAULT_MAINTENANCE_PLAN ON OFF
2 INTERNAL_PLAN ON OFF
3 INTERNAL_PLAN ON OFF
3 rows selected.
STAT_NAME VALUE
---------------------------------------------------------------- ----------------------------------------
NUM_CPUS 4
NUM_CPU_CORES 2
NUM_CPU_SOCKETS 1
3 rows selected.
(今回の検証環境の CDB$ROOT で、CDB$ROOT の CPU_COUNT を 1 にした4セッションの実験の直後に実行した結果です。PDB の実験の前に、この RESET で戻しています。PDB19 の中の RESET も同じ書き方で、こちらも cpu_count は 4、PDB19 の行の INSTANCE_CAGING は OFF に戻りました)
4 に戻り、INSTANCE_CAGING は OFF、ISMODIFIED も FALSE に戻りました。
大将「鍋4個に戻りました! 何もなかったことに!」
おうどん「ISMODIFIED まで FALSE に戻るのは、ちょっと潔すぎない?」
……変更の跡が消えるので、「誰がいつ変えたか」は別の場所(アラートログなど)で追うことになります。
確認と変更の手順:確認用と変更用を分ける
ここまでの結果を、作業の手順にまとめます。
1. 確認用(変更の前に流す)
上の「元に戻す」で流した3つの SELECT が、そのまま確認用になります。
V$PARAMETERのcpu_count:Oracle が使っている数。ISDEFAULT = TRUEなら OS に合わせているV$PARAMETERのresource_manager_plan:いまプランが有効か。SCHEDULER[...]で始まっていたら、スケジューラのウィンドウが設定したものV$RSRC_PLANのINSTANCE_CAGING:いま上限がかかっているか(コンテナごと)V$OSSTATのNUM_CPUS:OS から見えている数(CPU_COUNT の上限)
加えて、DBA_SCHEDULER_WINDOWS で、メンテナンス・ウィンドウの時間帯とプランを見ておきます。プランが有効になる時間帯に、CPU_COUNT の上限がかかるからです。
新人さん「確認、4つもあるんですか」
おうどん「張り紙、店長の出勤表、実際に鍋が絞られているか、コンロの数。4つ」
……どれか1つ飛ばすと、土曜の朝に電話が鳴る。
2. 変更用(作業メモのひな形)
■ 目的:インスタンス・ケージングで、このインスタンスの CPU を 2 スレッドぶんに制限する
■ 対象:cpu_count(CDB$ROOT / インスタンス全体。PDB だけなら PDB の中で実行)
■ 種類:動的(ISSYS_MODIFIABLE = IMMEDIATE)
■ 前提:NUM_CPUS が 2 以上。CPU のディレクティブを持つプランが有効になる時間帯を確認済み
■ 影響範囲:プランが有効な時間帯は、インスタンス全体の CPU 使用量が 2 スレッドぶんに制限される
オプティマイザ・パラレル実行・リソース・マネージャが使う値も変わる
■ 変更:alter system set cpu_count = 2 comment = '変更の理由と日付' scope = both;
■ 反映:すぐ。プランが有効なら、その場で INSTANCE_CAGING = ON
■ 変更後の確認:V$PARAMETER の cpu_count = 2、V$RSRC_PLAN の INSTANCE_CAGING、
V$SPPARAMETER の cpu_count = 2
■ 戻し方:変更前に V$SPPARAMETER で ISSPECIFIED = FALSE なら
alter system reset cpu_count scope = spfile; と alter system reset cpu_count scope = memory;
(変更前に値が書いてあったなら、その値を scope = both で設定し直す)
(作業メモのひな形です。この中の cpu_count = 2 と scope = both・scope = spfile の行は、今回は実行していません。今回実行したのは scope = memory での変更と reset ... scope = memory です)
パラレル実行(1つの SQL を複数のプロセスで分けて処理する仕組み)やオプティマイザも CPU_COUNT を使うので、上限を目的に変えたつもりでも、実行計画やパラレル度が変わる可能性があります。今回は実行計画の変化までは確かめていません。
新人さん「上限をかけるだけのつもりでした」
おうどん「大将の段取りにも使われる数だからね。鍋の数を変えたら、仕込みの量も変わる」
……「上限だけ変えたい」なら、張り紙1枚にいろんな意味が書いてあることを思い出そう。
ここから先は上級者向け。読み飛ばしてもOKです。
上級者向け:インスタンス・ケージングの設計と見張り方
パーティショニングとオーバーサブスクライブ
管理者ガイドのインスタンス・ケージングの節では、1台のサーバーに複数のインスタンスを載せるときの考え方が2つ紹介されています。
| やり方 | CPU_COUNT の合計 | 向いている場面(管理者ガイドの説明) |
|---|---|---|
| パーティショニング | サーバーの CPU の数と同じ | 重要な本番。インスタンス同士が影響しあわず、性能が予測しやすい |
| オーバーサブスクライブ | サーバーの CPU の数より大きい | 開発・テストや、負荷の低い重要でない本番 |
例として、16 CPU のサーバーで 8・4・2・2 と分ける(パーティショニング)、4 CPU のサーバーで4インスタンスにそれぞれ 3 ずつ与える(オーバーサブスクライブ)、が載っています。
大将たち(4店舗)「1台の厨房を、4店舗で分けて使います!」
おうどん「鍋の合計が12個で、コンロは4口。……それ、全員同時に忙しくなったらどうするの」
……オーバーサブスクライブは「全員同時には忙しくならない」という賭け。だから開発・テスト向きなんです。
利用率の上限と組み合わせたとき
管理者ガイドには、インスタンス・ケージングとプランの利用率の上限(UTILIZATION_LIMIT)を組み合わせたとき、上限は割り当てた CPU に対する割合で計算される、とあります。CPU_COUNT が 4 で、あるコンシューマ・グループ(セッションのグループ)の上限が 50% なら、そのグループは最大で 2 CPU ぶん、という例です。
新人さん「サーバーの 50% だと思ってました」
おうどん「4口のうちの半分じゃなくて、張り紙に書いた 4 個のうちの半分」
……割合の話は、まず「何の」割合かを聞こう。
絞られているかを見るビュー
管理者ガイドでは、確認用のビューとして次が紹介されています(今回の検証で使ったのは V$RSRC_PLAN だけです)。
V$RSRC_PLAN:INSTANCE_CAGING(ケージングが有効か)、CPU_MANAGED(CPU を管理しているか)V$RSRCMGRMETRIC:コンシューマ・グループごとのCPU_CONSUMED_TIME(使った CPU 時間)とCPU_WAIT_TIME(絞られて待った時間)。NUM_CPUS列は、リソース・マネージャが管理している CPU の数
V$OSSTAT にも RSRC_MGR_CPU_WAIT_TIME(リソース・マネージャのせいで CPU を待った時間の合計)があります。今回の実験の「経過時間は長いのに CPU 時間は短い」は、この待ちにあたる、と考えられます(今回は V$RSRCMGRMETRIC と RSRC_MGR_CPU_WAIT_TIME の値は取っていません)。
店長「待たせた時間は、ちゃんと記録しています」
おうどん「記録するくらいなら、もうちょっと優しく……いや、それが仕事か」
……絞るのが仕事の人に、絞らないでとは言えない。
管理者ガイドの注意:OS 側の制限と重ねない
同じ章の「OS のリソース制御を使うときのガイドライン」には、インスタンス・ケージングは使える CPU が CPU_COUNT と同じだけあると想定していて、それより少ないと、リソース・マネージャがプランの配分をうまく守れない、と書かれています。VM や OS の機能で CPU を絞ったうえに、CPU_COUNT でも絞る、という二重がけのときは、どちらが効いているかを分けて考えましょう。
RAC と特殊なインスタンス
CPU_COUNT のリファレンスでは、RAC(複数のサーバーで1つの DB を動かす構成)では全インスタンスで同じ値を使う、とされています。また、明示的に設定しないときの既定の上限が、Oracle ASM Proxy インスタンスは 2、Oracle ASM インスタンスは 4、Oracle IOServer インスタンスは 8、とも書かれています。どちらも今回の検証環境(シングルインスタンス)では確かめられないので、リファレンスの記載のみです。
新人さん「ASM は最大4って、なんか控えめですね」
おうどん「裏方は、鍋を奪い合わない約束なんだろうね」
……裏方が表の鍋を全部使ったら、お客さんのうどんが出てきません。
CPU_MIN_COUNT
今回の 19.3 の V$PARAMETER には、cpu_min_count(値 4、既定値)もありました。CPU_COUNT のリファレンスでは、PDB がいつでも必要とする最小の CPU の数を指定するパラメータとして紹介されています。この記事では動きは確かめていません。
🍜 まとめ:OCPU を聞かれたら、4段で答える
冒頭の新人さんの「4 ですよね?」に、いまならこう答えられます。
新人さん「OCPU を 4 にしてもらいました」
おうどん「x86 なら vCPU は 8。でも CPU_COUNT は、DB の中で NUM_CPUS と cpu_count を見てから答えよう」
新人さん「見ました。NUM_CPUS も cpu_count も 8 で、ISDEFAULT は TRUE です」
おうどん「完璧。OS に合わせて追いかけてる。あとは、誰かが固定していないかと、土曜の朝の店長だね」
(会話は説明用の架空の場面です。数字は例です)
……鍋の数を聞かれたら、大家さんとの契約、コンロ、鍋、張り紙、店長の出勤表。全部見てから答えるのが、厨房の常識です。
CPU の数は4段で数える。そして、CPU_COUNT は「上限」ではなく、プランが有効なときだけ上限になる数。
新人さん「4段、全部見ました! ……ところで、うちのサーバー、コンロは何口でしたっけ」
おうどん「NUM_CPU_CORES。さっき見たやつ」
……鍋を数えるのに夢中で、コンロを見忘れる。厨房あるあるです。
まずは、自分の DB で NUM_CPUS と cpu_count と INSTANCE_CAGING を、1回だけ並べて見てください。
3つとも思ったとおりだったら、安心してかけうどんでもどうぞ🍜
参考資料
- Oracle Database 19c Reference:CPU_COUNT — CPU_COUNT の意味(CPU またはスレッドの数)、既定値 0 と OS への追従、0 以外での固定、ALTER SYSTEM と PDB での変更、RESOURCE_MANAGER_PLAN があるときのインスタンス・ケージング、PDB の CPU_COUNT による制限、実際の数を超える値はエラー(パラメータファイルでは頭打ち)、RAC で同じ値、ASM などの既定の上限、CPU_MIN_COUNT
- Oracle Database 19c Reference:V$OSSTAT — NUM_CPUS・NUM_CPU_CORES・NUM_CPU_SOCKETS・NUM_LCPUS・NUM_VCPUS・RSRC_MGR_CPU_WAIT_TIME の意味、全プラットフォームで必ずあるのは NUM_CPUS と RSRC_MGR_CPU_WAIT_TIME だけであること
- Oracle Database 19c Administrator’s Guide:Managing Resources with Oracle Database Resource Manager — プランを指定しないとリソース・マネージャは有効にならないこと、メンテナンス・ウィンドウでは DEFAULT_MAINTENANCE_PLAN で有効になること、インスタンス・ケージングの有効化(CPU ディレクティブを持つプラン+CPU_COUNT)、パーティショニングとオーバーサブスクライブ、利用率の上限との組み合わせ、V$RSRC_PLAN・V$RSRCMGRMETRIC、OS のリソース制御との関係
- Oracle Cloud Infrastructure Documentation:Compute Shapes — OCPU は物理コア、vCPU は実行スレッド1本であること、x86 は 1 OCPU = 2 vCPU、Arm A1 は 1 OCPU = 1コア = 1 vCPU、Arm A2・A4 は 1 OCPU = 2コア = 2 vCPU
確認日:2026-10-17。

コメント