【図解】データベースのパフォーマンス設計|Oracle・PostgreSQLのCPU・メモリ・I/O

はじめに

Webシステムの応答時間が長くなると、TomcatのThread数やJVMのHeap容量が注目されがちです。しかし、Requestの大半がデータベースの処理完了を待っている場合、Application Server側の設定だけを変更しても改善しません。

データベースのパフォーマンス設計では、SQLの処理特性を把握し、CPU・メモリ・ストレージの処理能力と、Transaction・同時実行数を整合させることが重要です。Connectionを増やす前に、1件のSQLがどれだけのリソースを消費し、どこで待つ可能性があるかを理解する必要があります。

この記事では、Webシステムのデータベース処理に着目し、Oracle DatabaseとPostgreSQLに共通する性能設計の考え方と、両製品の実装上の違いを解説します。SQLの処理構造からCPU・メモリ・Storage I/O、Transaction、Connection Pool、性能試験、監視までを一通り整理します。

本記事の前提:Linux上のOracle DatabaseおよびPostgreSQLを使用する一般的なWebシステムを想定します。パラメータの既定値や利用可能な機能は製品・バージョン・Editionによって異なります。PostgreSQLの監視ビューもバージョンにより異なるため、使用環境の公式ドキュメントを確認してください。掲載する数値は説明用であり、一律の推奨値ではありません。

データベースのパフォーマンス設計とは

データベースの役割は、SQLを受け付け、必要なデータを読み書きし、Transactionの整合性と永続性を維持することです。性能要件としては、単位時間あたりの処理件数(TPS)、SQLや業務Transactionの応答時間、同時実行数などを定義します。

観点設計で決めること主な確認指標
Throughput通常時・ピーク時に必要な処理能力TPS、SQL実行数
LatencySQL・Transactionに許容する時間平均、p95 / p99、待機時間
CPU / MemorySQL処理とキャッシュに割り当てる容量CPU時間、キャッシュ、作業用メモリ
Storageデータ・ログ書き込みを支える能力IOPS、Throughput、Latency
Concurrency同時に処理するSQLとTransactionの量Active Session、接続数、Lock待ち

重要なのは、Connection数やキャッシュ容量といった個別の設定値を最大化することではありません。必要なTPSと応答時間を満たしながら、ピーク時や障害時にも処理能力の余裕を確保することが目標です。

TPSと応答時間・同時実行数の関係

TPSだけでは性能を判断できません。例えば、同じ500TPSでも、業務Transactionの平均応答時間が20msと200msでは、システム内に滞留する処理数が異なります。安定した状態ではLittle’s Law(平均同時滞留数 ≒ 到着率 × 平均滞留時間)が目安になります。ただし、DB接続数と同時滞留数は同一ではなく、待機やアプリケーション処理も区別して測定します。

データベースの処理構造

まず、ApplicationからSQLが届いてから結果が返るまでの処理を整理します。以下は共通的な概念図であり、OracleとPostgreSQLで内部のProcess構成やキャッシュ管理は異なります。

SQL処理とCPU・メモリ・ストレージの関係

同じSELECTでも、Indexを利用して少数の行を取得する場合と、大量の行を読み込んでSortする場合では、CPU・メモリ・I/Oの消費量が大きく異なります。UPDATEやINSERTでは、データの変更に加えてREDO / WALの生成やLockも関係します。

処理主なリソース性能設計のポイント
Parse / PlanCPU、共有メモリ不要な再解析、統計情報、実行計画
Scan / JoinCPU、キャッシュ、I/Oアクセスする行数、Index、Join方式
Sort / HashCPU、作業用メモリ、一時I/Oメモリ不足によるDisk Spill
UPDATE / INSERTCPU、キャッシュ、ログI/O更新量、Index維持、COMMIT
同時実行CPU、メモリ、Lock競合と待ち行列

参考までに、OracleとPostgresの処理構造の違いを以下に記載します。

Oracle DatabaseとPostgreSQLの内部処理構造の比較

CPUの性能設計

データベースのCPUは、SQLの解析・実行、行の評価、Join、Sort、集約、データの圧縮などに使われます。SQLの実行回数だけでなく、1回あたりのCPU消費量がサイジングに影響します。

たとえば、1TransactionのDB側CPU時間の合計が平均10msで、毎秒500件を処理する場合、毎秒5 CPU秒が必要です。概算では5 Coreを100%使用する計算に相当します。ただし、これは待機時間を含まないCPU需要の理論値です。5 Coreを割り当てれば500TPSを安定して処理できるという意味ではなく、OS・DBのバックグラウンド処理、並列実行、ピーク変動、CPU使用率の余裕を別途考慮します。

観点OraclePostgreSQL
SQL実行Server Processなどが実行通常は接続ごとのBackend Processが実行
並列処理Parallel ExecutionParallel Query
主な確認対象DB CPU、Active Session、Wait EventProcess CPU、pg_stat_activity、pg_stat_statements

並列度を上げると一つのSQLの処理時間が短くなる場合がありますが、同時実行される別のSQLに使えるCPUが減ることもあります。オンライン処理では、個々のSQLの短縮だけでなく、システム全体のTPSとp95 / p99を評価します。

メモリの性能設計

データベースでは、データのキャッシュとSQL実行時の一時的な作業領域にメモリを使用します。OracleとPostgreSQLはメモリの構成が異なるため、パラメータを単純に1対1で置き換えることはできません。

用途OraclePostgreSQL
共有領域SGA共有メモリ
データキャッシュDatabase Buffer Cacheshared_buffersとOS Page Cache
SQL共有情報Shared Poolなど各Backendの管理情報や共有キャッシュなど
作業用メモリPGAwork_memなど
ログバッファRedo Log BufferWAL Buffers

Oracle:SGAとPGA

SGAはInstance内で共有するメモリ領域で、Database Buffer Cache、Shared Pool、Redo Log Bufferなどを含みます。一方、PGAはServer Processなどが使用するメモリで、SortやHash Joinなどの作業にも関係します。

SGA_TARGET、SGA_MAX_SIZE、PGA_AGGREGATE_TARGETなどは、使用するメモリ管理方式と合わせて検討します。PGAのTargetはすべての状況で使用量を厳密に制限する値ではないため、同時実行数と実測値も確認します。

PostgreSQL:共有メモリとBackendごとのメモリ

PostgreSQLでは、shared_buffersがデータページの共有キャッシュとして機能します。SortやHashなどの作業にはwork_memが関係し、メンテナンス処理にはmaintenance_work_memなどを使用します。

work_memはConnectionごとに一度だけ確保される固定容量ではありません。 一つのQueryに複数のSort / Hash処理があり、さらに複数のQueryが並列実行されれば、合計使用量が増えます。Hash処理では、通常、work_mem × hash_mem_multiplierまで使用でき、並列QueryではWorkerごとにも作業メモリを使用します。work_memに最大接続数を単純に掛けるだけでは、ピーク時のメモリ消費を正確に見積もれません。代表的なSQLの実行計画、Sort・Hashの数、並列度、同時実行数を用いて評価します。

Buffer Cacheの設計

Buffer Cacheは、頻繁に参照するデータページをメモリに保持し、ストレージからの読み込みを減らすための仕組みです。ただし、キャッシュ容量を増やすだけでは、非効率なSQLや大量の不要なデータアクセスを解決できません。

観点OraclePostgreSQL
DB側のデータキャッシュDatabase Buffer Cacheshared_buffers
OSとの関係通常のDB File I/O方式に応じてOS Cacheの関与が異なる一般にOS Page Cacheも利用する
設計の要点SGA全体の配分とWorking Setshared_buffersとOS・他Processのメモリ配分

キャッシュヒット率は参考指標ですが、値が高いだけで十分とは判断できません。実際の物理読み込み量、SQL応答時間、ストレージLatency、キャッシュに収まらないWorking Setを合わせて評価します。

Storage I/Oの性能設計

データベースのI/Oは、データページの読み書き、Transaction Log、一時領域、Checkpointなどに分けて考えます。これらはI/Oサイズや同期性が異なるため、単一のIOPSだけで必要性能を決めることはできません。

I/O主な処理設計で確認すること
Data ReadCache Miss、ScanRead IOPS、サイズ、Latency
Data WriteDirty Pageの書き出しWrite Throughput、ピーク、平準化
Redo / WAL変更履歴の永続化同期書き込みLatency、連続書き込み
Temporary I/OSort / HashのDisk Spill一時領域の容量とI/O性能
Checkpoint変更済みページの書き出し書き込み集中時の余力

ストレージの性能設計では、通常時の平均値だけでなく、Batchとオンライン処理の重複、Checkpoint、Backup、障害・縮退時の負荷を考慮します。RAIDや共有ストレージを利用する場合は、通常時の実効性能に加え、ディスクやStorage Pathの障害時に残存リソースで必要なIOPS・帯域・Latencyを維持できるか確認します。

REDO / WALとCheckpointの設計

データベースは更新したデータページを毎回直ちにData Fileへ書き込むわけではありません。一般的には変更履歴を先にログへ記録し、変更済みページは後から書き出します。OracleではREDO、PostgreSQLではWALがこの役割を担います。

REDO・WALの永続化とデータページの書き出し

上図は通常の同期COMMITを単純化したものです。OracleではLGWRがREDOをオンラインREDOログに書き込み、PostgreSQLではWALの必要な範囲をFlushします。通常、COMMITのたびに変更済みデータページのData Fileへの書き出し完了を待つわけではありません。変更済みページの書き出しは後続のバックグラウンド処理やCheckpointなどで進みます。ログの永続化を緩める設定は障害時のデータ保護要件にも影響するため、性能だけで判断しません。

Checkpointによる書き込み負荷

Checkpointでは、変更済みページの書き出しや復旧に必要な情報の管理が行われます。書き込みが短時間に集中すると、通常のSQLとストレージを奪い合い、応答時間が悪化する可能性があります。

OracleではDBWRやCheckpoint関連の処理、PostgreSQLではCheckpointerやBackground Writerなどの動作を理解し、ログ生成量・書き込み量・ストレージ性能を合わせて設計します。PostgreSQLのmax_wal_sizeやcheckpoint_timeoutは、Checkpointの発生に関係する代表的な設定です。checkpoint_completion_targetはCheckpointの書き込みを時間的に分散する目標を指定します。なお、max_wal_sizeは厳密なWAL使用量の上限ではなく、レプリケーションスロットやアーカイブ処理などによってWALがさらに保持される場合があります。

設定主な役割設計上の注意
max_wal_sizeWAL量に基づくCheckpoint発生の目安厳密な容量上限ではない
checkpoint_timeout時間に基づくCheckpoint間隔の上限頻度と復旧時間のバランス
checkpoint_completion_targetCheckpointの書き込みを分散する目標I/O負荷とCheckpoint完了時間を確認

SQLと実行計画の設計

CPUやメモリを増強する前に、SQLが必要以上の行やページを読み込んでいないかを確認します。同じ結果を返すSQLでも、アクセス経路やJoin方式によって必要なリソースは大きく変わります。

設計項目確認すること注意点
Index検索条件、選択性、取得件数更新時の維持コストも増える
Join結合順序、件数、方式見積行数の誤差が計画に影響
Sort / Aggregate処理行数、作業メモリDisk Spillで一時I/Oが増える
統計情報データ分布、更新頻度古い統計情報で非効率な計画になる場合がある
結果件数不要な列・行を取得していないかDBだけでなくNetworkやJVMにも影響

OracleとPostgreSQLの実行計画

OracleではOptimizerが統計情報などを基に実行計画を選択し、PostgreSQLではPlanner / Optimizerが同様の役割を担います。どちらもIndexを使えば常に速いわけではありません。大量の行を取得する場合、Full ScanやSequential Scanの方が適することもあります。

-- PostgreSQL:実行計画と実測を確認する例
EXPLAIN (ANALYZE, BUFFERS)
SELECT * FROM orders WHERE customer_id = 100;

EXPLAIN ANALYZEはSQLを実際に実行します。更新系SQLや高負荷の本番環境では副作用と実行負荷に注意してください。Oracleでは、DBMS_XPLAN.DISPLAY_CURSORなどを利用して実行計画を確認できます。実測行数や実行統計を確認するには、統計収集の設定や必要な権限を確認します。どちらの製品でも、推定行数と実測行数、実行時間、読み込んだブロック数などを比較し、想定外のFull ScanやJoin、Sortを調査します。

TransactionとLockの設計

SQL単体が高速でも、複数のTransactionが同じデータを更新するとLock待ちが発生します。長時間Transactionは、他の処理の待ち時間だけでなく、不要になった行Versionの管理やログ・復旧にも影響します。

観点設計で確認すること
Transaction時間DB接続を保持したまま外部APIや利用者入力を待たないか
更新順序複数Transactionが同じ表・行を異なる順序で更新しないか
Isolation Level業務整合性と同時実行性の要件に合っているか
Lock待ち競合する行、待機時間、Timeoutを把握できるか
長時間TransactionOracleのUNDO、PostgreSQLのVACUUMへの影響を考慮したか

OracleとPostgreSQLはいずれもMVCCに関連する仕組みを持ちますが、OracleはUNDO情報、PostgreSQLは行VersionとVACUUMなど、実装が異なります。特にPostgreSQLでは長時間Transactionが古い行Versionの回収を妨げる可能性があるため、定期Batchや接続Poolの運用も含めて設計します。

Connection・Sessionと同時実行数の設計

ここまでで、SQL一件あたりのリソース消費と、CPU・メモリ・I/Oの処理能力を整理しました。その結果を基に、同時にどれだけのSQLやTransactionを実行させるかを決めます。

観点OraclePostgreSQL
接続・処理の構造SessionとServer Processなど。Dedicated / Shared Server構成で異なる通常は接続ごとにBackend Processを使用
主な上限PROCESSES、SESSIONSなどmax_connectionsなど
設計上の注意Process / Session数とPGAなどのメモリBackend数と各Processのメモリ・CPU負荷

Connection数と実行中SQL数は同じではありません。 接続を保持していても待機中の場合があり、反対に少数のConnectionでも重いSQLが並列に実行されればCPUやI/Oが飽和します。接続数だけでなく、Activeな処理と待機時間を測定します。

TomcatのConnection Poolとの整合

Tomcat側のDB Connection Poolは、データベースへ同時に流す処理量を制御する重要な要素です。Poolを大きくすると接続待ちが減る場合がありますが、DB側でCPU・I/O・Lock競合が増えれば、SQLの実行時間が長くなる可能性があります。

TomcatのConnection PoolとDBの同時実行数

例えばTomcatのmaxThreadsが400、DB Connection Poolの上限が100でも、常に300 Threadが待つわけではありません。DBへアクセスしないRequestや、Connectionを保持していない処理もあります。Poolが満杯になると、接続を取得しようとするRequestは待機し、Tomcatの処理Threadを占有することがあります。SQLの遅延がConnection保持時間を延ばし、接続待ちが増えてプール枯渇に至ることもあります。実際のDB利用率、Connection保持時間、待機時間、SQLの処理能力からPool容量を決めます。

複数のTomcat NodeやBatch、運用ツールが同じDBを利用する場合は、それぞれのPool上限を合算したピーク接続数と、同時実行時の負荷を考慮します。DBの上限値までPoolを割り当てず、管理接続や障害対応の余裕も残します。

Connection Poolが飽和するまでの流れ

SQLの実行遅延 → Connection保持時間の増加 → PoolのActive接続数が上限に到達 → 接続取得待ちの増加 → Requestの応答遅延・Timeoutという連鎖が起きることがあります。Pool上限を増やす前に、SQLの実行時間、Lock待ち、接続返却漏れ、DB側のCPU・I/O飽和を確認します。

主要パラメータの設計

性能パラメータは、先に必要な処理能力とメモリ配分を定義し、その結果を設定へ反映します。以下は設計対象となる代表例です。既定値や変更方法は使用するバージョンの公式マニュアルで確認してください。

設計観点OraclePostgreSQL
共有メモリSGA_TARGET、SGA_MAX_SIZEshared_buffers
作業用メモリPGA_AGGREGATE_TARGETwork_mem、maintenance_work_mem
接続数PROCESSES、SESSIONSmax_connections
並列処理PARALLEL関連設定max_parallel_workers_per_gatherなど
ログ・CheckpointREDO Log構成、Checkpoint関連設定max_wal_size、checkpoint_timeout、checkpoint_completion_target
統計情報Optimizer StatisticsANALYZE、autovacuum関連設定

パラメータをまとめて変更すると、改善・悪化の原因が分からなくなります。変更前のTPS、SQL応答時間、CPU、I/O、待機時間を残し、仮説ごとに比較します。

障害・縮退時の性能設計

通常時の性能要件を満たしていても、NodeやStorage Pathの障害、Standbyへの切り替え、Replicaの停止によって残存リソースへ負荷が集中する場合があります。性能設計では、可用性だけでなく、切り替え後の処理能力も確認します。

事象性能面の確認事項
Primary停止・切り替え切り替え先のCPU・メモリ・I/O、Connection再確立
Replica停止参照処理の振り分け先、Primaryへの追加負荷
Storage Path障害残存Pathの帯域・Latency、I/O Timeout
復旧後CacheのWarm-up、Replicationの追いつき、負荷の再分散

OracleのData Guard、PostgreSQLのStreaming Replicationなどは構成や同期方式によって性能特性が異なります。同期転送ではCommit Latency、非同期転送では遅延やデータ保護要件とのTrade-offを検討します。利用する製品・構成で実際に縮退試験を行います。

データベースの性能試験

性能試験では、DB単体のBenchmarkだけでなく、Tomcatからの実際のSQL構成、Transaction比率、データ量を再現します。Read中心かWrite中心か、検索条件の分布、Hot Rowへの更新集中などによって結果が変わります。

試験主な確認内容
通常負荷目標TPS、SQL応答時間、CPU・I/O
ピーク負荷同時実行数、Lock、接続待ち、p95 / p99
段階的な負荷増加処理能力の飽和点と待機時間の増加
長時間試験Cache、Checkpoint、VACUUM、ログ増加
Batch重複オンライン処理へのCPU・I/O・Lock影響
障害・縮退Failover後のTPS、応答時間、復旧時間

合格条件は、例えば「通常時の目標TPSを達成し、業務Transactionのp95 / p99が要件以内、Error率が許容範囲内」といったサービス要件から定義します。DBのCPU使用率やキャッシュヒット率だけを合否基準にしません。負荷生成側が先に限界へ達していないことも確認します。

性能設計を確認する

設計値が妥当かどうかは、DB内部の統計情報とOSの観測値を合わせて確認します。

観点OraclePostgreSQL
接続・実行中SQLV$SESSION、V$PROCESSpg_stat_activity
SQL負荷V$SQL、実行計画pg_stat_statements、EXPLAIN
待機・競合Wait Event、ASH / AWR(利用条件に注意)wait_event、pg_locksなど
I/ODB統計とOSのiostatなどpg_stat_io(対応バージョン)、OSのiostatなど
ログ・CheckpointREDO生成量、書き込み待機WAL統計、Checkpoint統計

PostgreSQL:SQL負荷と実行中の処理

-- 現在の接続状態と待機状況
SELECT state, wait_event_type, wait_event, count(*)
FROM pg_stat_activity
GROUP BY state, wait_event_type, wait_event
ORDER BY count(*) DESC;

-- pg_stat_statementsが有効な環境で負荷の大きいSQLを確認
SELECT calls, total_exec_time, mean_exec_time, query
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10;

pg_stat_statementsは拡張機能の導入と必要な設定が前提です。統計値は累積期間やリセット時刻を確認し、短い計測区間の負荷を評価する場合は差分を使います。SQL本文には業務情報が含まれる場合があるため、取得・保管権限を管理します。

Oracle:SessionとWait Event

-- 実行中Sessionの状態を確認する例
SELECT sid, serial#, status, event, wait_class, sql_id
FROM v$session
WHERE type = 'USER'
  AND status = 'ACTIVE';

STATUS = 'ACTIVE'はCPU実行中だけを意味しません。Active SessionがすべてCPUで実行中とは限りません。I/OやLockなどを待っている場合もあるため、Wait EventとSQLを関連付けて確認します。AWR / ASHなどの利用にはEditionやライセンス条件が関係する場合があるため、利用環境の契約を確認してください。

Oracle RAC:Instance別にActive Sessionを確認する

Oracle RAC環境では、単一InstanceのV$SESSIONだけでなく、GV$SESSIONを使うとInstance別のActive Sessionを確認できます。ActiveにはCPU実行中と待機中の両方が含まれます。

SELECT inst_id, status, wait_class, event, COUNT(*) AS session_count
FROM gv$session
WHERE type = 'USER' AND status = 'ACTIVE'
GROUP BY inst_id, status, wait_class, event
ORDER BY inst_id, session_count DESC;

まとめ

データベースの性能設計では、まずSQLの処理構造を理解し、CPU・メモリ・Storage I/Oの必要量を見積もります。そのうえでTransactionとLock、Connection数を設計し、Tomcatから流入する負荷との整合を取ります。

設計項目確認すること
CPUSQL一件あたりのCPU消費量とピークTPS
MemoryBuffer Cache、作業用メモリ、同時実行時の使用量
StorageData / REDO・WAL / Temporary I/Oの特性
SQL実行計画、Index、統計情報、不要なデータアクセス
TransactionLock競合、長時間Transaction、COMMIT
ConnectionTomcatのPoolとDB側の処理能力
障害時切り替え先の処理能力と復旧時の負荷

Connectionを増やす前に、SQL一件のコストを把握する。メモリを増やす前に、何がキャッシュされ、何がDiskへ書き出されるかを理解する。通常時だけでなく、ピークと縮退時の処理能力を検証する。 これがOracleとPostgreSQLに共通する性能設計の基本です。

設計した処理能力を超えるRequestが流入する場合は、Connection Poolの上限やTimeoutを適切に設定し、必要に応じてRate LimitやBackpressureによってDBへの負荷を制御します。Retryを導入する際は、障害時に再試行が集中してDBの負荷を増幅させないよう、再試行回数や間隔も設計します。

参考文献

コメント