【Oracle】TEMP表領域の肥大化の原因とクリーンアップ方法完全ガイド|確認SQL・RESIZE・再作成・予防策まで

【Oracle】TEMP表領域の肥大化原因とクリーンアップ方法 Oracle

Oracleを運用していると、ある日突然「TEMP表領域がいっぱいになった」「ORA-01652が発生した」という事態に直面することがあります。TEMP表領域(一時表領域)は、ソートやハッシュ結合などで一時的に使用される領域ですが、適切に管理しないと肥大化してシステム障害の原因になります。

この記事では、TEMP表領域が肥大化する根本原因から、確認SQL・RESIZE・再作成による具体的なクリーンアップ方法、ORA-01652エラーへの対処、PGAチューニングによる予防策まで、実務で役立つ手順を体系的に解説します。

この記事でわかること

  • TEMP表領域の仕組みと役割
  • 肥大化の主な原因(ソート・ハッシュ結合・PGA不足など)
  • v$sort_usage・v$temp_space_headerを使った使用状況確認SQL
  • セッション強制終了・RESIZE・再作成の3つのクリーンアップ手順
  • ORA-01652エラーの対処法
  • PGA_AGGREGATE_TARGETによる肥大化予防策
スポンサーリンク

TEMP表領域とは何か

TEMP表領域(一時表領域)は、Oracleがクエリを実行する際に一時的なデータを格納するための専用領域です。通常のデータファイル(datafile)とは異なり、TEMPFILEとして管理されます。

TEMP表領域が使用される主なケース

操作 説明
ORDER BY / GROUP BY ソート処理でPGAに収まらない場合にディスクへスピルアウト
ハッシュ結合(Hash Join) ハッシュテーブルがPGAを超えた際にTEMPを使用
インデックス再構築 CREATE INDEX・REBUILD時に一時領域を大量消費
一時表(Global Temporary Table) セッション/トランザクション単位の一時表データ
サブクエリのマテリアライズ 複雑なSQLの中間結果を一時保存

PGAとTEMPの関係

OracleはまずPGA(Program Global Area)と呼ばれるメモリ領域でソートを試みます。PGAの容量(PGA_AGGREGATE_TARGETで制御)を超えた場合、残りの処理をTEMP表領域(ディスク)に「スピルアウト」して処理します。

TEMP表領域の自動解放について
通常、TEMP表領域はクエリ終了後に自動的に解放されます。しかし、セッションが異常終了した場合や、ソートセグメントが残ったままになるケースでは、使用済みとして予約されたブロックが残ることがあります。

TEMP表領域が肥大化する主な原因

TEMP表領域が予想以上に大きくなる原因は複数あります。それぞれの原因を理解することで、適切な対策が取れます。

1. 大量データのソート・ハッシュ結合

数百万件以上のデータをORDER BYやGROUP BYで処理する場合、PGAに収まらず大量のTEMP領域を消費します。特にバッチ処理や夜間のETL処理で起きやすいパターンです。

2. インデックスの再構築

ALTER INDEX ... REBUILDは内部的にソートを伴うため、大きなインデックスほどTEMP消費量が増大します。オンライン再構築(ONLINEオプション)でも同様です。

3. PGA_AGGREGATE_TARGETの不足

PGAのメモリが小さく設定されていると、小さなソート処理でもすぐにTEMPへスピルアウトします。多数のセッションが同時に処理を行うと、TEMP消費が累積して肥大化します。

4. セッション異常終了による残存

通常はセッション終了とともにTEMPセグメントが解放されますが、ネットワーク切断・プロセスクラッシュなどの異常終了では、OSレベルのセッションは消えてもOracleのTEMPセグメントが残存することがあります。

注意:TEMP領域は「使用済み予約」と「実際に使用中」が異なる
v$temp_space_headerで確認できるused_blocksには、すでに処理が終わったが解放されていないブロックも含まれます。これは正常動作ですが、新しい割り当てが必要な場合は再利用されます。問題は、OSレベルでファイルサイズが縮小しない点です。

5. 並列クエリ(Parallel Query)

並列度(PARALLEL)を高く設定したクエリは、複数のパラレルスレーブが同時にTEMPを使用するため、消費量が並列度に比例して増大します。

TEMP使用状況の確認SQL

クリーンアップを行う前に、現在のTEMP使用状況を正確に把握することが重要です。以下のSQLでセッション別・表領域全体の使用量を確認します。

セッション別TEMP使用量(v$sort_usage)

-- セッション別TEMP使用量(MB単位)
SELECT
    s.sid,
    s.serial#,
    s.username,
    s.status,
    u.tablespace,
    u.segtype,
    ROUND(u.blocks * 8192 / 1024 / 1024, 2) AS used_mb,
    s.sql_id
FROM v$sort_usage u
JOIN v$session s ON u.session_addr = s.saddr
ORDER BY used_mb DESC;
各列の説明

  • sid / serial#:セッションを一意に識別するID
  • segtype:SORT(ソート)またはHASH(ハッシュ結合)など
  • used_mb:そのセッションが使用中のMB数
  • sql_id:TEMPを消費している現在のSQL

TEMP表領域全体の使用量(v$tempseg_usage)

-- TEMP表領域全体の使用量集計
SELECT
    tablespace_name,
    ROUND(SUM(bytes) / 1024 / 1024, 2) AS used_mb
FROM v$tempseg_usage
GROUP BY tablespace_name;

TEMP表領域の空き容量確認(v$temp_space_header)

-- TEMP表領域のブロック使用状況
SELECT
    tablespace_name,
    file#,
    total_blocks,
    used_blocks,
    free_blocks,
    ROUND(used_blocks / total_blocks * 100, 1) AS used_pct
FROM v$temp_space_header
ORDER BY tablespace_name, file#;

TEMPファイルのサイズ確認(DBA_TEMP_FILES)

-- TEMPFILEのサイズと自動拡張設定
SELECT
    file_name,
    tablespace_name,
    ROUND(bytes / 1024 / 1024, 0) AS size_mb,
    ROUND(maxbytes / 1024 / 1024, 0) AS maxsize_mb,
    autoextensible
FROM dba_temp_files
ORDER BY tablespace_name;

クリーンアップ方法1:セッションを強制終了

現在進行中のセッションが大量のTEMPを消費している場合、そのセッションを強制終了することでTEMPを解放できます。

手順1:対象セッションを特定する

-- TEMPを大量消費しているセッションとSQLを特定
SELECT
    s.sid,
    s.serial#,
    s.username,
    s.osuser,
    s.program,
    ROUND(u.blocks * 8192 / 1024 / 1024, 2) AS temp_mb,
    q.sql_text
FROM v$sort_usage u
JOIN v$session s ON u.session_addr = s.saddr
LEFT JOIN v$sql q ON s.sql_id = q.sql_id AND s.sql_child_number = q.child_number
ORDER BY temp_mb DESC;

手順2:セッションをKILLする

-- セッションを強制終了(SIDとSERIAL#は手順1で確認した値)
ALTER SYSTEM KILL SESSION '123,456' IMMEDIATE;
IMMEDIATEオプションについて
IMMEDIATEを付けると、Oracleはセッションを即座に切断し、使用中のリソース(ロック・TEMPセグメント)を解放します。IMMEDIATEなしの場合は、次にそのセッションがOracleと通信したときに終了します。急ぎの場合は必ずIMMEDIATEを付けてください。
注意:業務への影響を確認してから実行する
セッション強制終了は、そのセッションが実行中のトランザクションをすべてロールバックします。業務アプリケーションのセッションを誤ってKILLすると、処理中データが失われる可能性があります。usernameprogramosuserを必ず確認してから実行してください。

クリーンアップ方法2:TEMPFILEのリサイズ(RESIZE)

TEMPファイルが必要以上に大きくなっている場合、ALTER DATABASE TEMPFILE ... RESIZEでファイルサイズを縮小できます。ただし、使用中のブロックが含まれている場合はエラーになります。

TEMPFILEのパスを確認する

-- TEMPFILEのパスとサイズを確認
SELECT file_name, ROUND(bytes/1024/1024,0) AS current_mb
FROM dba_temp_files;

TEMPFILEをリサイズする

-- TEMPFILEを512MBにリサイズ(パスは環境に合わせて変更)
ALTER DATABASE TEMPFILE '/u01/oradata/ORCL/temp01.dbf' RESIZE 512M;
注意:使用中ブロックがあるとORA-03297エラーになる
「ORA-03297: file contains used data beyond requested RESIZE value」が出た場合は、指定サイズより後方にまだ使用中(または予約済み)のブロックが存在することを意味します。その場合はいったんセッションを終了させてから再試行するか、次の「再作成」の方法を使ってください。

RESIZE後のサイズ確認

-- リサイズ後の確認
SELECT file_name, ROUND(bytes/1024/1024,0) AS new_mb
FROM dba_temp_files;

クリーンアップ方法3:一時表領域の再作成(確実な方法)

RESIZEが失敗する場合や、確実にTEMP領域を初期化したい場合は、新しい一時表領域を作成してデフォルトを切り替え、古い表領域を削除する方法が最も確実です。

手順1:新しい一時表領域を作成する

-- 新しい一時表領域TEMP2を作成(サイズは環境に合わせて調整)
CREATE TEMPORARY TABLESPACE temp2
    TEMPFILE '/u01/oradata/ORCL/temp02.dbf' SIZE 1G AUTOEXTEND ON;

手順2:データベースのデフォルト一時表領域を変更する

-- データベース全体のデフォルトTEMPをTEMP2に変更
ALTER DATABASE DEFAULT TEMPORARY TABLESPACE temp2;

-- 変更後の確認
SELECT property_value FROM database_properties
WHERE property_name = 'DEFAULT_TEMP_TABLESPACE';

手順3:特定ユーザーのTEMPが旧表領域のままの場合は変更する

-- TEMPを指定しているユーザーを確認
SELECT username, temporary_tablespace FROM dba_users
WHERE temporary_tablespace = 'TEMP';

-- ユーザーのデフォルトTEMPを変更
ALTER USER scott TEMPORARY TABLESPACE temp2;

手順4:古い一時表領域を削除する

-- 旧TEMPを削除(INCLUDING CONTENTS AND DATAFILESでファイルも削除)
DROP TABLESPACE temp INCLUDING CONTENTS AND DATAFILES;

-- 削除確認
SELECT tablespace_name FROM dba_tablespaces
WHERE tablespace_name LIKE 'TEMP%';
再作成のポイント
一時表領域の再作成手順のポイントをまとめます。

  • 必ず新規作成→デフォルト変更→削除の順で実施する(逆順は不可)
  • デフォルトTEMP変更後もユーザー個別にTEMPが設定されている場合は別途変更が必要
  • INCLUDING DATAFILESを付けるとOSのファイルも自動削除される(ファイルシステムの空き確保に有効)
  • 新TEMPのサイズは、通常時のTEMP使用量の1.5〜2倍を目安に設定する

肥大化の予防策

TEMP表領域の肥大化を予防するには、SQLの最適化とOracleパラメータの適切な設定が重要です。

1. PGA_AGGREGATE_TARGETの増加

PGAが十分に大きければ、TEMP表領域へのスピルアウトを減らせます。システムのメモリ量に応じて適切な値を設定します。

-- 現在のPGA_AGGREGATE_TARGETを確認
SHOW PARAMETER pga_aggregate_target;

-- PGA_AGGREGATE_TARGETを2GBに増加(再起動不要)
ALTER SYSTEM SET pga_aggregate_target = 2G SCOPE=BOTH;

2. WORKAREA_SIZE_POLICYをAUTOに確認・設定

-- WORKAREA_SIZE_POLICYの確認(AUTOが推奨)
SHOW PARAMETER workarea_size_policy;

-- AUTOに設定(PGAの自動管理を有効化)
ALTER SYSTEM SET workarea_size_policy = AUTO SCOPE=BOTH;
WORKAREA_SIZE_POLICY=AUTO の効果
AUTOに設定すると、OracleはPGA_AGGREGATE_TARGETの範囲内で各セッションのソート・ハッシュ結合用メモリを自動的に管理します。MANUALの場合はSORT_AREA_SIZEなどを個別に設定する必要があり、チューニングが複雑になります。特別な理由がない限りAUTOを推奨します。

3. SQLの最適化

不要なORDER BYを削除する、適切なインデックスを作成してフルスキャンを排除するなど、根本的なSQLのチューニングが最も効果的です。

-- TEMPを多く消費しているSQL(実行計画でSORT操作を確認)
SELECT sql_id, sql_text, executions,
       ROUND(disk_reads / NULLIF(executions,0), 0) AS disk_reads_per_exec
FROM v$sql
WHERE disk_reads > 10000
ORDER BY disk_reads DESC
FETCH FIRST 20 ROWS ONLY;

4. 定期的な監視スクリプト

TEMP使用量を定期的に監視し、閾値を超えたらアラートを出す仕組みを整えることで、肥大化を早期に検知できます。

-- TEMP使用率が80%を超えたらアラート対象として抽出
SELECT tablespace_name,
       ROUND(used_blocks / total_blocks * 100, 1) AS used_pct,
       free_blocks
FROM v$temp_space_header
WHERE used_blocks / total_blocks > 0.8;

ORA-01652エラーの対処法

ORA-01652: unable to extend temp segment by N in tablespace TEMPは、TEMP表領域の空き領域が不足してセグメントを拡張できないエラーです。

エラー発生時の即時対処

方法A:既存TEMPFILEを拡張する

-- 既存TEMPFILEのサイズを拡張
ALTER DATABASE TEMPFILE '/u01/oradata/ORCL/temp01.dbf' RESIZE 4G;

-- または自動拡張をONにする
ALTER DATABASE TEMPFILE '/u01/oradata/ORCL/temp01.dbf'
    AUTOEXTEND ON MAXSIZE 8G;

方法B:TEMPFILEを追加する

-- 既存TEMP表領域に新しいTEMPFILEを追加
ALTER TABLESPACE temp
    ADD TEMPFILE '/u01/oradata/ORCL/temp02.dbf' SIZE 2G AUTOEXTEND ON;
ORA-01652が繰り返し発生する場合
エラーが繰り返す場合は、単なる容量不足ではなく、以下の可能性を検討してください。

  • 特定のSQL/バッチ処理がTEMPを異常消費している(v$sort_usageで特定)
  • PGA設定が小さすぎてすべての処理がTEMPにスピルアウトしている
  • 並列度が高すぎるクエリが実行されている

よく使うSQL一覧

目的 ビュー/コマンド 備考
セッション別TEMP使用量確認 v$sort_usage + v$session TEMP消費元SQLも取得可能
TEMP表領域全体の使用量 v$tempseg_usage MB単位で集計
ブロックレベルの使用状況 v$temp_space_header used_pct計算に使用
TEMPファイルのサイズ・設定 dba_temp_files 自動拡張の確認
セッション強制終了 ALTER SYSTEM KILL SESSION IMMEDIATEを推奨
TEMPファイルのリサイズ ALTER DATABASE TEMPFILE RESIZE 使用中ブロックがあるとエラー
新TEMP表領域の作成 CREATE TEMPORARY TABLESPACE デフォルト変更と組み合わせる
デフォルトTEMPの変更 ALTER DATABASE DEFAULT TEMPORARY TABLESPACE 全ユーザーに影響
旧TEMP表領域の削除 DROP TABLESPACE … INCLUDING DATAFILES OSファイルも削除
PGA設定変更 ALTER SYSTEM SET pga_aggregate_target 再起動不要(SCOPE=BOTH)

FAQ

Q. TEMP表領域は自動で解放されないのですか?
A. 通常は、クエリやトランザクションが正常に終了すると自動的に解放されます。ただし、セッションが異常終了(ネットワーク切断・プロセスクラッシュなど)した場合は、Oracleがセッションのリソースを非同期でクリーンアップするまでTEMPセグメントが残ることがあります。また、OSのファイルサイズ自体はOracleが内部的に再利用するだけで縮小しないため、「ファイルが大きいままだが実際は空き状態」というケースも多いです。
Q. TEMPFILEを追加すると劇的に改善されますか?
A. ORA-01652が頻発している場合はTEMPFILE追加で即時解消できます。ただし、根本原因がSQL非効率やPGA不足にある場合は、容量を増やしても一時的な対処に過ぎません。まずv$sort_usageでどのSQLが大量消費しているかを特定し、SQLチューニングやPGA増加と組み合わせることで、恒久的な改善が期待できます。
Q. Oracle 12c以降でTEMP表領域の管理が変わりましたか?
A. Oracle 12c以降ではCDB(コンテナDB)/PDB(プラガブルDB)構成が導入され、TEMPの管理単位がCDB全体またはPDB個別になりました。PDBごとにデフォルトTEMPを設定できるため、マルチテナント環境ではPDB単位の管理が重要です。また、12.2以降では共有TEMPファイル(Shared Temp)の機能が強化され、CDB全体でTEMPを効率的に共有できます。
Q. TEMP使用が多い原因となるSQLを特定するには?
A. v$sort_usagev$sessionをJOINしてsql_idを取得し、そのsql_idをv$sqlで検索するとSQL文が確認できます。さらにDBMS_XPLAN.DISPLAY_CURSORで実行計画を確認し、SOPTやHASH JOINのuses-temp-spaceの有無を確認します。AWRレポートの「Top SQL by Physical Reads」セクションも参考になります(AWRレポートの読み方参照)。
Q. TEMP表領域がいっぱいで DROP TABLESPACE できない場合は?
A. DROP TABLESPACEは対象の表領域が「デフォルトTEMP」に設定されていると実行できません。まず別のTEMP表領域を作成してデフォルトを切り替え(ALTER DATABASE DEFAULT TEMPORARY TABLESPACE)、すべてのユーザーのTEMPも変更してから実行してください。また、DROPはTEMPFILE内に使用中セグメントがない状態でのみ成功します。v$sort_usageでセッションが残っていないか確認してから実行します。

まとめ

TEMP表領域の肥大化対策まとめ

  • 確認v$sort_usageでセッション別、v$temp_space_headerで全体のTEMP使用状況を把握する
  • 即時対処:消費中のセッションをALTER SYSTEM KILL SESSION ... IMMEDIATEで強制終了
  • サイズ縮小ALTER DATABASE TEMPFILE ... RESIZEでファイル縮小(使用中ブロックがある場合はエラー)
  • 確実な初期化:新TEMP作成→デフォルト変更→旧TEMP削除の順で再作成
  • 予防策PGA_AGGREGATE_TARGETを増やしてスピルアウトを減らす、SQLチューニングで不要なソートを削除する
  • ORA-01652:TEMPFILEのリサイズまたは追加で対応し、根本原因のSQL特定も実施

あわせて読みたい