【完全ガイド】ORA-04031: unable to allocate shared memory の原因と解決方法|Shared Pool・Hard Parse・Bind変数まで徹底解説

【完全ガイド】ORA-04031: unable to allocate shared memory の原因と解決方法|Shared Pool・Hard Parse・Bind変数まで徹底解説

目次

Oracle DBA が深夜3時に叩き起こされる代表的な本番障害エラー:

ORA-04031: unable to allocate 4096 bytes of shared memory 
("shared pool","SELECT * FROM orders WHERE id=12345","sql area","kglh0")

「共有メモリを割り当てられない」というシンプルなメッセージの裏で、実はDB がほぼ機能停止する重大な障害。新しい SQL パースができず、PL/SQL がロードできず、業務システムがフリーズします。

現場では:

  • アラートログに ORA-04031 の嵐
  • アプリが接続できない
  • 開発者からの緊急連絡
  • とりあえず FLUSH SHARED_POOL で凌ぐ
  • 数日後にまた発生
  • SGA を増やしても解決しない
  • Hard Parse が原因と気づいた頃には手遅れ

このエラーの怖いところは、単なるメモリ不足ではなく、**Shared Pool の断片化(フラグメンテーション)**が真の原因のことが多く、サイズを増やしても再発する点。Oracle DBA のベテランでも、以下のような誤解に陥りがちです:

  • 「メモリを増やせば解決」→ 数日後に再発
  • 「FLUSH で解決」→ 一時しのぎ、根本原因未解決
  • 「Bind 変数使えばいいんでしょ」→ アプリ改修が必要で工数不足
  • 「CURSOR_SHARING = FORCE で万事解決」→ 実は副作用あり

本記事では、ORA-04031: unable to allocate shared memory完全な原因と解決方法を、リファレンスとして実用的に整理します。Shared Pool のアーキテクチャ、エラーメッセージの読み方、5大原因、6つの解決策(Flush・サイズ増加・Reserved Pool・Bind 変数・CURSOR_SHARING・Pinning)、ORA-04030 との違い、V$SGASTAT/V$SHARED_POOL_ADVICE 診断、実践シナリオ、予防のベストプラクティス、FAQまで完全網羅。この1本で ORA-04031 を根本から解決できるようになります。


結論:緊急対応と根本対応の2段構え

時間がない方向けに、最速の対処を先に示します。

緊急対応(本番障害中)

-- 1. Shared Pool フラッシュ(即効性、一時しのぎ)
ALTER SYSTEM FLUSH SHARED_POOL;

-- 2. 効かない場合は restart
SHUTDOWN IMMEDIATE;
STARTUP;

⚠️ FLUSH は根本解決ではありません。数日で再発します。

根本対応 6手段

#対応効果副作用
Bind 変数使用(アプリ改修)⭐⭐⭐⭐⭐アプリ改修必要
CURSOR_SHARING = FORCE⭐⭐⭐⭐一部プラン変化
Shared Pool サイズ増加⭐⭐⭐一時しのぎ
Reserved Pool 拡張⭐⭐⭐静的、再起動要
Package Pinning⭐⭐⭐⭐大きい PKG のみ
FLUSH SHARED_POOL⭐⭐一時的

診断コマンド(決定版)

-- Shared Pool の内訳
SELECT pool, name, ROUND(bytes/1024/1024, 2) AS mb
FROM v$sgastat 
WHERE pool = 'shared pool'
ORDER BY bytes DESC
FETCH FIRST 20 ROWS ONLY;

-- Hard parse 率
SELECT ROUND(
  (SELECT value FROM v$sysstat WHERE name = 'parse count (hard)') * 100 /
  NULLIF((SELECT value FROM v$sysstat WHERE name = 'parse count (total)'), 0), 2
) AS hard_parse_pct FROM DUAL;
-- 5% 超えたら要注意、10% 超えたら緊急

詳細は以下で解説します。


まず理解する:Shared Pool の仕組み

エラーメッセージの構造

ORA-04031: unable to allocate <BYTES> bytes of shared memory 
           ("<POOL>","<OBJECT>","<HEAP>","<COMMENT>")
  • BYTES: 割り当てようとした量
  • POOL: shared pool / large pool / java pool / streams pool
  • OBJECT: SQL テキスト / PL/SQL パッケージ名
  • HEAP: sql area / KGL heap 等の内部ヒープ
  • COMMENT: 詳細

最も重要なのは POOL の値。ここでどの領域が枯渇したか判別。

SGA の構造

[SGA (System Global Area)]
├── Shared Pool         ← ORA-04031 の主犯
│   ├── Library Cache   (SQL/PLSQL のキャッシュ)
│   ├── Row Cache       (データディクショナリ)
│   └── Free Memory
├── Buffer Cache        (データキャッシュ)
├── Redo Log Buffer
├── Large Pool          ← RMAN、並列処理用
├── Java Pool
├── Streams Pool
└── Result Cache        (12c+)

Shared Pool の役割

  • SQL の実行計画をキャッシュ
  • PL/SQL パッケージをキャッシュ
  • データディクショナリをキャッシュ

これらが繰り返し使われることで、パフォーマンスが向上します。

Hard Parse vs Soft Parse

SQL 実行
  ↓
Library Cache にキャッシュ済み?
  ↓ Yes                    ↓ No
Soft Parse                  Hard Parse
(高速、メモリ確保少)        (遅い、メモリ大量確保)

Hard Parse が大量発生すると、Shared Pool が断片化し、ORA-04031 につながる。

なぜ「フラグメンテーション」が起こるか

Shared Pool のメモリは可変サイズのチャンクで管理:

初期: [大きな空き領域]
     ↓ SQL 1 パース (4KB)
     [SQL1 (4KB)][大きな空き領域]
     ↓ SQL 2 パース (8KB)
     [SQL1 (4KB)][SQL2 (8KB)][空き領域]
     ↓ SQL 1 破棄
     [空き 4KB][SQL2 (8KB)][空き領域]
     ↓ SQL 3 パース (12KB)
     → 連続 12KB がない → ORA-04031

断片化により、合計は足りるが連続空きがない状態に。


エラーメッセージの読み解き方

パターンA: Shared Pool

ORA-04031: unable to allocate 4096 bytes of shared memory 
("shared pool","...","sql area","kglh0")
  • shared pool + sql area or KGL heap = Hard Parse ストームが最有力

パターンB: Large Pool

ORA-04031: unable to allocate ... in "large pool"
  • RMAN バックアップ
  • 並列処理
  • Shared Server 接続

パターンC: Java Pool

ORA-04031: unable to allocate ... in "java pool"
  • Oracle Java の使用

パターンD: Streams Pool

ORA-04031: unable to allocate ... in "streams pool"
  • GoldenGate / Streams / Data Guard

内部ヒープ名の意味

ヒープ意味
sql areaSQL 実行計画キャッシュ
KGL heapLibrary Cache 内部
KGH heapメモリ管理内部
PL/SQL areaPL/SQL コンパイル結果
row cacheデータディクショナリ

【原因①】Hard Parse ストーム(最頻出)

症状

-- リテラル値を毎回変えて実行
SELECT * FROM orders WHERE id = 12345;
SELECT * FROM orders WHERE id = 12346;
SELECT * FROM orders WHERE id = 12347;
-- ... 毎回 Hard Parse、Shared Pool 消耗

各 SQL が別物として扱われ、Shared Pool を汚染

診断

-- リテラル SQL の存在確認
SELECT sql_text, executions, parse_calls
FROM v$sql
WHERE parsing_schema_name = 'APP_USER'
ORDER BY parse_calls DESC
FETCH FIRST 10 ROWS ONLY;

-- 類似 SQL のグループ化
SELECT plan_hash_value,
       ROUND(SUM(sharable_mem)/1024/1024, 2) AS mem_mb,
       COUNT(1) AS versions
FROM v$sql
GROUP BY plan_hash_value
ORDER BY 2 DESC
FETCH FIRST 10 ROWS ONLY;
-- versions が多い → リテラル SQL の証拠

Hard Parse 率確認

SELECT ROUND(
  (SELECT value FROM v$sysstat WHERE name = 'parse count (hard)') * 100 /
  NULLIF((SELECT value FROM v$sysstat WHERE name = 'parse count (total)'), 0), 2
) AS hard_parse_pct FROM DUAL;
  • 1% 未満: 健全
  • 1-5%: 監視要
  • 5-10%: 危険
  • 10% 超: 緊急対応

解決A: Bind 変数使用(本命)

// ❌ アプリ側で悪い書き方
String sql = "SELECT * FROM orders WHERE id = " + orderId;

// ✅ Bind 変数
String sql = "SELECT * FROM orders WHERE id = ?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setInt(1, orderId);
# ❌ 悪い
cursor.execute(f"SELECT * FROM orders WHERE id = {order_id}")

# ✅ 良い
cursor.execute("SELECT * FROM orders WHERE id = :id", id=order_id)

アプリ改修が必要だが、根本解決

解決B: CURSOR_SHARING = FORCE

アプリ改修せずに Oracle 側で対処:

-- 現在の値
SELECT value FROM v$parameter WHERE name = 'cursor_sharing';

-- 設定
ALTER SYSTEM SET cursor_sharing = 'FORCE' SCOPE = BOTH;

動作:

WHERE customer_id = 10045
    ↓ Oracle が自動置換
WHERE customer_id = :SYS_B_0

メリット: アプリ改修不要 デメリット: 一部の SQL で最適な実行計画が選ばれない可能性

パラメータ変更の詳細は Oracle パラメータ確認(V$PARAMETER)の記事も参照してください。

解決C: Rails での対応

Rails ActiveRecord は基本的に Bind 変数を使うため大丈夫:

# ActiveRecord は自動で Bind 変数化
User.where(id: user_id).first
# → SELECT * FROM users WHERE id = :a1

ただし、SQL 直書きは注意:

# ❌ 悪い
User.where("id = #{user_id}").first

# ✅ 良い
User.where("id = ?", user_id).first
User.where(id: user_id).first

Rails 8 系の詳細は Rails 8 アップグレードガイドの記事、find/find_by/where の記事、Solid Queue 使い方の記事も参照してください。


【原因②】Shared Pool フラグメンテーション

症状

-- Shared Pool に大量の使用中領域あり、合計は空いてる
-- でも連続領域が確保できない

診断

-- Free Memory の状況
SELECT pool, name, ROUND(bytes/1024/1024, 2) mb
FROM v$sgastat
WHERE pool = 'shared pool' AND name IN ('free memory', 'sql area')
ORDER BY bytes DESC;

-- Reserved Pool の断片化
SELECT free_space, used_space, request_failures, 
       last_failure_size, max_free_size
FROM v$shared_pool_reserved;
-- request_failures が多く、max_free_size が小さい → 断片化

解決A: FLUSH SHARED_POOL(一時対処)

ALTER SYSTEM FLUSH SHARED_POOL;

即効性あり根本解決ではない

副作用:

  • 全ての SQL が再パース → 一時的に負荷急増
  • 業務時間帯は避ける

解決B: Reserved Pool 拡張

-- 現在
SHOW PARAMETER shared_pool_reserved_size

-- 拡張(静的、要再起動)
ALTER SYSTEM SET shared_pool_reserved_size = 32M SCOPE = SPFILE;

大きなオブジェクトの一時領域として使う。

解決C: 根本対応(Bind 変数化)

フラグメンテーションの根本原因はリテラル SQL。原因①の対策と同じ。


【原因③】Shared Pool サイズ不足

症状

-- Shared Pool の使用率が高い
SELECT ROUND(
  (SELECT SUM(bytes) FROM v$sgastat WHERE pool = 'shared pool' AND name != 'free memory') /
   SUM(bytes) * 100, 2) AS used_pct
FROM v$sgastat WHERE pool = 'shared pool';

診断

-- Shared Pool Advisor
SELECT shared_pool_size_for_estimate AS size_mb,
       shared_pool_size_factor,
       estd_lc_time_saved,
       estd_lc_load_time
FROM v$shared_pool_advice
ORDER BY shared_pool_size_for_estimate;

推奨サイズが得られる。

解決

-- 現在
SHOW PARAMETER shared_pool_size

-- 増加
ALTER SYSTEM SET shared_pool_size = 4G SCOPE = BOTH;

-- SGA_TARGET 使用時は SGA_TARGET を増やす
ALTER SYSTEM SET sga_target = 16G SCOPE = BOTH;

Automatic Memory Management(SGA_TARGET / MEMORY_TARGET)の場合、Oracle が自動で調整。

適切なサイズ

OLTP システム: 2〜8GB、リテラル SQL 多いなら 12GB+

サイズだけでは解決しない:Bind 変数化を並行推奨。


【原因④】Large Pool 不足

症状

ORA-04031: unable to allocate ... in "large pool"

原因

  • RMAN バックアップ
  • 並列処理(PARALLEL)
  • Shared Server 接続
  • Data Pump

診断

SELECT pool, name, ROUND(bytes/1024/1024, 2) mb
FROM v$sgastat WHERE pool = 'large pool'
ORDER BY bytes DESC;

解決

ALTER SYSTEM SET large_pool_size = 512M SCOPE = BOTH;

Data Pump 使用時は Oracle Data Pump 使い方の記事、Data Pump 実行時のリソース逼迫関連は Oracle Tablespace 管理の記事、ORA-01652: unable to extend temp segment の記事も参照してください。


【原因⑤】PL/SQL パッケージのリロード

症状

大きな PL/SQL パッケージが繰り返しロード/アンロード:

SELECT namespace, name, sharable_mem, loads, executions
FROM v$db_object_cache
WHERE type IN ('PACKAGE', 'PACKAGE BODY')
ORDER BY loads DESC
FETCH FIRST 10 ROWS ONLY;

loads が多い(何度もリロード)+ sharable_mem が大きい = 問題。

解決:Package Pinning

メモリに固定して再ロード防止:

-- 固定
EXEC DBMS_SHARED_POOL.KEEP('SYS.DBMS_STATS', 'P');
EXEC DBMS_SHARED_POOL.KEEP('SYS.STANDARD', 'P');
EXEC DBMS_SHARED_POOL.KEEP('APP_USER.MY_PACKAGE', 'P');

-- 解除
EXEC DBMS_SHARED_POOL.UNKEEP('APP_USER.MY_PACKAGE', 'P');

-- 固定状況確認
SELECT owner, name, type, kept 
FROM v$db_object_cache 
WHERE kept = 'YES';

Oracle が再起動するまで固定される。

起動時の自動 Pinning

CREATE OR REPLACE TRIGGER pin_packages
AFTER STARTUP ON DATABASE
BEGIN
  DBMS_SHARED_POOL.KEEP('SYS.STANDARD', 'P');
  DBMS_SHARED_POOL.KEEP('SYS.DBMS_STATS', 'P');
  DBMS_SHARED_POOL.KEEP('APP_USER.MY_LARGE_PACKAGE', 'P');
END;
/

ORA-04031 vs ORA-04030 の違い

メモリ系の兄弟エラーだが、原因が違う:

エラー対象メモリ原因対処
ORA-04031SGA (共有)Shared Pool 逼迫SGA 増加、Bind 変数
ORA-04030PGA (プロセス)プロセスメモリPGA 増加、SQL チューニング

ORA-04030 メッセージ例

ORA-04030: out of process memory when trying to allocate 
           16328 bytes (pga heap,control file record buffe)

プロセス個別のメモリが枯渇。OS のメモリと直接関連。

見分け方

  • shared pool / SGA heap → ORA-04031
  • pga heap / cga heap → ORA-04030

診断ツール完全リファレンス

V$SGASTAT(内訳確認)

SELECT pool, name, 
       ROUND(bytes/1024/1024, 2) AS mb,
       ROUND(bytes * 100 / SUM(bytes) OVER (PARTITION BY pool), 2) AS pct
FROM v$sgastat
WHERE pool = 'shared pool'
ORDER BY bytes DESC;

V$SHARED_POOL_ADVICE(推奨サイズ)

SELECT shared_pool_size_for_estimate mb,
       ROUND(shared_pool_size_factor, 2) factor,
       estd_lc_size,
       estd_lc_time_saved
FROM v$shared_pool_advice
ORDER BY shared_pool_size_for_estimate;

V$SHARED_POOL_RESERVED(Reserved Pool 状況)

SELECT free_space, avg_free_size, free_count,
       used_space, avg_used_size, used_count,
       requests, request_misses, 
       request_failures, last_failure_size, max_free_size
FROM v$shared_pool_reserved;

V$SQL(重複 SQL 検出)

-- リテラル SQL の証拠
SELECT force_matching_signature,
       COUNT(*) AS num_versions,
       ROUND(SUM(sharable_mem)/1024/1024, 2) AS total_mb,
       MIN(sql_text) AS sample_sql
FROM v$sql
WHERE force_matching_signature > 0
GROUP BY force_matching_signature
HAVING COUNT(*) > 100
ORDER BY total_mb DESC
FETCH FIRST 20 ROWS ONLY;

同じ signature で多数バージョンあるとリテラル SQL の証拠。

V$DB_OBJECT_CACHE(オブジェクトキャッシュ)

SELECT owner, name, type,
       ROUND(sharable_mem/1024/1024, 2) mb,
       loads, executions,
       kept
FROM v$db_object_cache
WHERE sharable_mem > 100000
ORDER BY sharable_mem DESC;

Library Cache Hit Ratio

SELECT namespace,
       gets, gethits, ROUND(gethits/gets*100, 2) AS hit_pct,
       pins, pinhits, ROUND(pinhits/pins*100, 2) AS pin_pct
FROM v$librarycache
ORDER BY gets DESC;

hit_pct が 95% 未満なら要注意。

Alert Log 監視

tail -f /u01/app/oracle/diag/rdbms/orcl/orcl/trace/alert_ORCL.log | grep ORA-04031

Linux のログ監視は、Linux find オプション一覧の記事、grep オプション一覧の記事、awk コマンドの記事、systemctl vs service の記事も参照してください。


実践シナリオ

シナリオ1:深夜3時の緊急対応

-- 1. 緊急対応
ALTER SYSTEM FLUSH SHARED_POOL;

-- 2. 状況把握
SELECT pool, name, ROUND(bytes/1024/1024, 2) mb
FROM v$sgastat WHERE pool = 'shared pool'
ORDER BY bytes DESC FETCH FIRST 10 ROWS ONLY;

-- 3. Hard Parse 率
SELECT ROUND(
  (SELECT value FROM v$sysstat WHERE name = 'parse count (hard)') * 100 /
   (SELECT value FROM v$sysstat WHERE name = 'parse count (total)'), 2
) AS pct FROM DUAL;

-- 4. Cursor Sharing を FORCE に(応急対応)
ALTER SYSTEM SET cursor_sharing = 'FORCE' SCOPE = BOTH;

シナリオ2:継続的な予兆監視

#!/bin/bash
# monitor_shared_pool.sh
sqlplus -s / as sysdba <<EOF
SET LINESIZE 200
SELECT ROUND(free/total*100, 2) AS free_pct
FROM (
  SELECT SUM(bytes) total,
         SUM(DECODE(name, 'free memory', bytes, 0)) free
  FROM v\$sgastat WHERE pool = 'shared pool'
) 
WHERE ROUND(free/total*100, 2) < 5;
EXIT;
EOF

crontab で5分間隔監視、閾値超過時メール通知。cron の使い方は crontab 使い方の記事、Linux バックグラウンド実行は Linux でプロセスをバックグラウンド実行する方法の記事も参照してください。

シナリオ3:Rails 大規模アプリ

# 多くの場合 ActiveRecord が Bind 変数化してくれる
User.where(id: params[:id]).first  # OK

# ただし SQL 直書き注意
User.where("id = #{params[:id]}").first  # NG(SQL Injection もリスク)
User.where("id = ?", params[:id]).first  # OK

# find_each で大量処理
User.where(status: 'active').find_each(batch_size: 1000) do |user|
  # 処理
end

シナリオ4:Java アプリ

// ❌ SQL Injection + ORA-04031 の温床
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM orders WHERE id = " + id);

// ✅ Bind 変数
PreparedStatement ps = conn.prepareStatement("SELECT * FROM orders WHERE id = ?");
ps.setInt(1, id);
ResultSet rs = ps.executeQuery();

シナリオ5:Python (oracledb)

import oracledb

# ❌ 悪い
cursor.execute(f"SELECT * FROM orders WHERE id = {order_id}")

# ✅ 良い
cursor.execute(
    "SELECT * FROM orders WHERE id = :id",
    id=order_id
)

シナリオ6:CI/CD デプロイでの負荷急増

# デプロイ後のスパイクで ORA-04031
# 事前対策:
# - Shared Pool 増加
# - Package Pinning 起動時 trigger
# - Cursor Sharing 見直し

Kamal デプロイの詳細は Kamal 2 デプロイの記事も参照してください。

シナリオ7:夜間バッチ時の RMAN + Large Pool

-- RMAN バックアップ用 Large Pool 拡張
ALTER SYSTEM SET large_pool_size = 512M SCOPE = BOTH;

-- 並列度制御
ALTER SYSTEM SET parallel_max_servers = 16 SCOPE = BOTH;

シナリオ8:Docker Oracle での対応

# docker-compose.yml
services:
  oracle:
    image: gvenzl/oracle-xe:21
    environment:
      ORACLE_SGA: 4096  # 4GB
      ORACLE_PGA: 2048  # 2GB

Docker 関連は docker daemon 接続エラーの記事、Docker no space left on device の記事も参照してください。

シナリオ9:AWS RDS Oracle

Parameter Group で:

  • shared_pool_size
  • sga_target
  • cursor_sharing

を設定。

シナリオ10:定期的な Package Pinning

-- 起動時トリガーで自動化
CREATE OR REPLACE TRIGGER pin_at_startup
AFTER STARTUP ON DATABASE
DECLARE
  CURSOR c_pkg IS
    SELECT owner, name, type 
    FROM dba_objects 
    WHERE type IN ('PACKAGE', 'PACKAGE BODY')
      AND owner IN ('SYS', 'APP_USER')
      AND object_name IN ('STANDARD', 'DBMS_STATS', 'MY_LARGE_PKG');
BEGIN
  FOR r IN c_pkg LOOP
    BEGIN
      DBMS_SHARED_POOL.KEEP(r.owner || '.' || r.name, 'P');
    EXCEPTION WHEN OTHERS THEN NULL;
    END;
  END LOOP;
END;
/

予防のベストプラクティス

1. Bind 変数の徹底

アプリ設計の基本。すべての DB アクセスで Bind 変数使用。

2. Shared Pool 監視

-- 定期実行
SELECT ROUND(SUM(DECODE(name, 'free memory', bytes, 0)) / SUM(bytes) * 100, 2) 
       AS free_pct
FROM v$sgastat WHERE pool = 'shared pool';

空き 5% 未満で警告。

3. Hard Parse 率監視

SELECT ROUND(
  (SELECT value FROM v$sysstat WHERE name = 'parse count (hard)') * 100 /
  NULLIF((SELECT value FROM v$sysstat WHERE name = 'parse count (total)'), 0), 2
) AS pct FROM DUAL;

5% 超で調査。

4. Package Pinning

大きな PL/SQL パッケージは起動時に固定。

5. Shared Pool サイズ設計

  • OLTP: SGA の 20-30%
  • DWH: SGA の 10-20%
  • Bind 変数徹底なら小さくても OK

6. CURSOR_SHARING の適切な選択

用途
EXACT(デフォルト)Bind 変数徹底されているなら最善
FORCEレガシーアプリの応急対応
SIMILAR廃止(12c 以降)、使わない

7. 定期的な V$SGASTAT レビュー

月次で Shared Pool の内訳を確認、異常な増加を早期発見。

8. アプリ開発者への教育

  • Bind 変数の重要性
  • SQL 直書きのリスク
  • パラメータ化クエリの標準化

9. Advisor の活用

-- Shared Pool Advisor
SELECT * FROM v$shared_pool_advice;
-- SGA Target Advisor
SELECT * FROM v$sga_target_advice;

10. Automatic Memory Management

-- 12c 以降推奨は個別指定
ALTER SYSTEM SET sga_target = 16G;
ALTER SYSTEM SET pga_aggregate_target = 4G;

パラメータ設定の詳細は Oracle パラメータ確認(V$PARAMETER)の記事、UNDO 領域関連は ORA-01555: snapshot too old のエラー記事、TEMP/一時領域関連は ORA-01652: unable to extend temp segment の記事、Tablespace 全般は Oracle Tablespace 管理の記事も参照してください。


トラブルシューティング

FLUSH しても数日で再発

根本原因が未解決。Bind 変数化 or CURSOR_SHARING = FORCE を検討。

SGA を増やしても再発

リテラル SQL が原因の場合、サイズだけでは解決しない。

CURSOR_SHARING = FORCE の副作用

  • 一部の SQL で実行計画が変わる
  • ヒストグラム使用時に問題

対処:

-- 特定 SQL のみ EXACT
SELECT /*+ CURSOR_SHARING_EXACT */ ...

AWR/ASH で調査

-- 過去24時間の ORA-04031
SELECT to_char(sample_time, 'YYYY-MM-DD HH24:MI') time,
       session_id, sql_id
FROM dba_hist_active_sess_history
WHERE event LIKE '%ORA-04031%'
  AND sample_time > SYSDATE - 1
ORDER BY sample_time;

Advanced Diagnostic Pack ライセンス必要。

AHF(Autonomous Health Framework)で診断

$ tfactl diagcollect -srdc ora4031

Oracle 純正診断ツール。My Oracle Support で自動分析可能。

DB 起動直後に発生

メモリ確保時の問題。SGA_MAX_SIZE / SGA_TARGET の設定確認、OS のメモリ状況確認。


よくある質問(FAQ)

Q1. FLUSH SHARED_POOL の副作用

全 SQL 再パース → 一時的に負荷急増。業務時間帯は避ける

Q2. Reserved Pool とは

大きなオブジェクト(5KB 以上)用の予約領域。断片化に強い。

Q3. Bind 変数化のトレードオフ

Bind 変数使うと、ヒストグラム統計が効かないケースあり。Bind Peeking で対処。

Q4. Auto SGA vs Manual

Auto SGA(SGA_TARGET)推奨。Manual(個別サイズ指定)は特殊要件時のみ。

Q5. 11g 以降の Adaptive Cursor Sharing

Bind 変数使用時も、値によって実行計画を変える機能。Bind Peeking の弊害を軽減

Q6. Result Cache との関係

SHOW PARAMETER result_cache

Result Cache 使いすぎで Shared Pool 圧迫することあり。適切なサイズ設定を。

Q7. Rails での Bind 変数使用状況

# 自動で Bind 変数化
User.where(id: 1).first

# 確認
User.where(id: 1).to_sql
# → "SELECT users.* FROM users WHERE users.id = 1"
# 実行時は Bind 変数化される

Q8. Autonomous DB での対応

Oracle が自動管理。ユーザーは基本的に対応不要。

Q9. RAC 環境での対応

インスタンスごとに Shared Pool が独立。片方だけ発生することもある:

SELECT inst_id, pool, name, ROUND(bytes/1024/1024, 2) mb
FROM gv$sgastat
WHERE pool = 'shared pool' AND name = 'free memory'
ORDER BY inst_id;

Q10. 監視ツール

  • Oracle Enterprise Manager
  • AHF (Autonomous Health Framework)
  • Cloud Control
  • サードパーティ (Datadog、New Relic 等)

Q11. Cursor 数の確認

SELECT username, COUNT(*)
FROM v$open_cursor
GROUP BY username
ORDER BY 2 DESC;

Q12. 短期対応 vs 長期対応

短期: FLUSH、SGA 増加、CURSOR_SHARING = FORCE 長期: アプリ改修(Bind 変数化)、Pinning


参考リンク

Oracle 公式


まとめ

ORA-04031: unable to allocate shared memory の解決、要点を再整理します。

エラーの本質

Shared Pool の連続空きメモリが不足
→ SQL パースや PL/SQL ロード不可
→ DB が実質的に機能停止

緊急対応

-- 即効性
ALTER SYSTEM FLUSH SHARED_POOL;

-- ダメなら restart
SHUTDOWN IMMEDIATE;
STARTUP;

根本原因(多くの場合)

リテラル SQL の大量発行
→ Hard Parse ストーム
→ Shared Pool フラグメンテーション
→ ORA-04031

6つの解決策

#手法効果
Bind 変数使用(アプリ改修)⭐⭐⭐⭐⭐
CURSOR_SHARING = FORCE⭐⭐⭐⭐
Shared Pool サイズ増加⭐⭐⭐
Reserved Pool 拡張⭐⭐⭐
Package Pinning⭐⭐⭐⭐
FLUSH SHARED_POOL⭐⭐

診断コマンド Top 3

-- ① Pool 内訳
SELECT pool, name, ROUND(bytes/1024/1024, 2) mb
FROM v$sgastat WHERE pool = 'shared pool'
ORDER BY bytes DESC FETCH FIRST 10 ROWS ONLY;

-- ② Hard Parse 率
SELECT ROUND(
  (SELECT value FROM v$sysstat WHERE name = 'parse count (hard)') * 100 /
   NULLIF((SELECT value FROM v$sysstat WHERE name = 'parse count (total)'), 0), 2
) AS pct FROM DUAL;

-- ③ リテラル SQL の証拠
SELECT force_matching_signature, COUNT(*) versions,
       ROUND(SUM(sharable_mem)/1024/1024, 2) mb
FROM v$sql
WHERE force_matching_signature > 0
GROUP BY force_matching_signature
HAVING COUNT(*) > 100
ORDER BY 3 DESC FETCH FIRST 10 ROWS ONLY;

ORA-04031 vs ORA-04030

  • 04031: SGA(共有)→ Shared Pool / Bind 変数対応
  • 04030: PGA(プロセス)→ プロセスメモリ / SQL チューニング

エラーメッセージから対応判別

"shared pool" → 主犯(Hard Parse ストーム疑い)
"large pool" → RMAN/並列/Shared Server
"java pool" → Oracle Java
"streams pool" → GoldenGate/Streams

予防のベストプラクティス

  • Bind 変数の徹底
  • Shared Pool 監視(空き 5% 未満で警告)
  • Hard Parse 率監視(5% 超で調査)
  • Package Pinning
  • CURSOR_SHARING 適切な選択
  • アプリ開発者教育
  • Advisor 活用
  • Automatic Memory Management

事故防止

  • FLUSH は一時しのぎ、根本対応必須
  • SGA サイズだけでは解決しない
  • CURSOR_SHARING = FORCE には副作用
  • 監視の自動化(早期検知)
  • AHF で診断簡素化

これらの知識は、Oracle 本番運用・障害対応・パフォーマンスチューニング・アプリ設計レビュー・Rails / Kamal 環境・Docker 開発など、あらゆる場面で活用できます。本記事をブックマークしておけば、ORA-04031 の緊急事態にも冷静に対処できるようになります。


本記事は2026年6月時点の情報をもとに、Oracle Database 19c〜23ai での動作確認・公式ドキュメントに基づき作成しています。Oracle のバージョンにより挙動が異なる場合があるため、最新の情報は Oracle 公式ドキュメント(docs.oracle.com)もあわせてご確認ください。