OracleのCPU_COUNTとOSのCPU数・OCPUは何が違う? V$OSSTATのNUM_CPUSで確かめる方法と、CPU_COUNTだけではCPUが制限されない理由(19c)

OracleのCPU_COUNTとOSのCPU数・OCPUは何が違う?

こんにちは、おうどんです🍜

新人さん「クラウドの 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_COUNTPDB の中の 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 A11コア = 1 vCPU
Arm A2・A42コア = 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つ。説明用の架空の場面です。

  1. 平日の昼に、誰かが「CPU は 2 個ぶんで十分」と CPU_COUNT = 2 を設定する
  2. 平日の昼はリソース・プランがないので、何も起きない。「影響なし」と報告される
  3. 夜22時や土曜6時にメンテナンス・ウィンドウが開いて、DEFAULT_MAINTENANCE_PLAN が有効になる
  4. その時間だけ、インスタンス全体が 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。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

おうどん|癒しと創作をたのしむ雑食クリエイター
写経アプリやクレイセラピー、優しい和風デザインがすき。
ZARDと刀剣と文字に癒されて、今はアプリ作ってます。

コメント

コメントする

目次