- Oracle Cloud
- Oracle Database
【OCI】BaseDBでDataPumpを利用する際のストレージ・サービス比較
BaseDBの運用でData Pump用の領域が不足した際、外部ストレージの活用が有効です。本記事では3つのストレージについて1TB利用時のコスト目安や性能、運用負荷を徹底比較します。自社環境に最適な外部ストレージ選びのポイントが分かります。
|
|
従来の「REFRESH ON COMMIT」のマテリアライズド・ビュー(以下、ON COMMIT MView)を運用していて、ピーク時間帯になるとCOMMITがなかなか返ってこない、AWRレポートを解析すると「enq: JI - contention」が待機イベントの上位を占めている――そんな経験はありませんか。
集計の鮮度を最優先したくて「多少コストがかかってもON COMMITでリアルタイムに反映したい」と設計したはずが、いざ本番運用が軌道に乗りユーザー数やトランザクション数が増えてくると、「コミットのたびにMViewリフレッシュが走り、更新処理そのもののスループットを押し下げてしまう」というジレンマに悩まされるケースは少なくありません。
本記事では、従来のON COMMIT MViewが抱えてきたこのようなスループット低下や待機イベントのボトルネックを振り返りつつ、Oracle AI Database 26aiが提供する「同時リフレッシュ(Concurrent Refresh)」によって、OLTP/DWHそれぞれのユースケースでどのような改善が見込めるのかを検証していきます。
Index
まずMViewとは、通常のビューとは異なり、クエリの結果をデータとして保持するビューです。
例えば、DWH系のデータベースであれば、ファクト表とディメンション表の結合や集計といった高負荷な処理を事前に結果として格納しておくことができます。
分散データベース構成でのユースケースも想定され、Database Linkと共に活用することでリモート・サイトのデータをローカル・サイトにレプリケーションし、検索時のネットワーク負荷を下げるような活用方法も考えられます。
MViewはクエリの結果をデータとして保持するため、定期的な同期(リフレッシュ)によってデータの鮮度を保つ必要があります。
リフレッシュのタイミングはMViewのCREATE文で指定でき、「REFRESH ON COMMIT」句を指定したものを本記事ではON COMMIT MViewと呼びます。
ON COMMIT MViewでは、データの基となるマスター表に対するトランザクションがコミットされるタイミングで、自動的にリフレッシュが行われます。
これはMView上のデータが、マスター表と常に同期していることを必須とする要件がある場合などによく採用される構成の一つです。
ただし、ON COMMIT MViewが常に最適解とは限らず、この構成ではリフレッシュ処理がコミット処理の一部として実行されるため、マスター表のコミットに要する時間が長くなる制約があります。
特に、同じMViewのマスター表を複数セッションから同時更新すると、集計処理の整合性を保つためにコミット時のリフレッシュ処理が、enq: JI - contention(JI enqueue待ち)で直列化されてしまう点が、高負荷時の深刻なボトルネックとなってしまいました。
|
ON COMMIT MViewのリフレッシュ動作イメージ |
前述のような長年の悩みを緩和する新機能として、26aiからON COMMIT MViewの同時リフレッシュ(Concurrent Refresh)が登場しました。
同時リフレッシュは、これまで直列化されていた複数のセッションからのリフレッシュ処理の同時(並列)実行を実現します。
この同時セッションの数に制限がないことも驚きの一つです。
従って、OLTP系の処理においてON COMMIT MViewを採用するケースや、マイクロバッチによって複数のセッションよりファクト表が更新され得るデータベースなどにおいて、最大限の力を発揮できるものと考えられます。
|
「同時リフレッシュ」有効時のON COMMIT MViewのリフレッシュ動作イメージ |
同時リフレッシュは、どのようなMViewの構成でも自由に利用できるわけではありません。
次の制限にご注意ください。
同時リフレッシュを活用することで、ON COMMIT MViewのマスター表に対する更新処理は、スループットがどのくらい向上するでしょうか。
今回は、OLTP相当の処理を想定した小さなトランザクションを多発させるケースと、DWHにおけるマイクロバッチ程度の規模を想定したまとまった更新単位のトランザクションのケースで動作検証を行いました。
このケースでは、全国チェーンの小売店におけるPOS売上データのリアルタイム集計基盤を想定して検証しました。
マスター表「SALES_MASTER」は、各レジで発生した売上明細(店舗ID、レジ端末ID、金額など)をINSERTするためのトランザクション・テーブルです。
SQL> create TABLE SALES_MASTER ( 2 ID number primary key, 3 BRANCH_ID number, 4 TERMINAL_ID number, 5 AMOUNT number, 6 SALES_DATE date 7 ); 表が作成されました。
このテーブルに対して、リアルタイム集計結果を保持するサマリー・テーブルとしてMViewを作成します。
店舗(BRANCH_ID)× 端末(TERMINAL_ID)ごとの合計金額(SUM)と件数(COUNT)を常に最新状態で保持することが狙いです。
まずは、MView「SALES_SUMMARY_MV」を従来の同時リフレッシュが無効なON COMMIT MViewとして作成します。
SQL> create MATERIALIZED VIEW LOG on SALES_MASTER 2 with ROWID, 3 SEQUENCE (BRANCH_ID, TERMINAL_ID, AMOUNT, SALES_DATE), 4 COMMIT SCN 5 including new values; マテリアライズド・ビュー・ログが作成されました。 SQL> create MATERIALIZED VIEW SALES_SUMMARY_MV 2 build immediate 3 REFRESH FAST ON COMMIT 4 as 5 select BRANCH_ID, TERMINAL_ID, count(*) as CNT, sum(AMOUNT) as TOTAL_AMOUNT 6 from SALES_MASTER 7 group by BRANCH_ID, TERMINAL_ID; マテリアライズド・ビューが作成されました。
OLTP相当の処理をイメージして、次のような処理を端末ごとのトランザクションと見立てて実行します。
[oracle@Lin96 tmp]$ cat ./oltp_test.sql set TIMING ON declare V_SID number; V_BASE_ID number; V_BRANCH_ID number; begin -- 自身のSIDを後続のINSERTで「TERMINAL_ID(レジ番号)」として扱う select SYS_CONTEXT('USERENV', 'SID') into V_SID from DUAL; V_BASE_ID := V_SID * 10000000; -- 1,000回の送信(COMMIT)ループ for i in 1..1000 LOOP -- 店舗IDは 1~10 の間でランダムに決定 V_BRANCH_ID := mod(i, 10) + 1; -- 取引明細を送信する insert into SALES_MASTER (ID, BRANCH_ID, TERMINAL_ID, AMOUNT, SALES_DATE) values ( V_BASE_ID + (i * 1000), V_BRANCH_ID, V_SID, 1000, SYSDATE ); COMMIT; end LOOP; end; / exit;
そして、上記の処理を並行して10セッションから実行させます。
これにより、全国の店舗からランダムに取り引きの記録が登録される状況を再現します。
[oracle@Lin96 tmp]$ cat ./run_oltp.sh
#!/bin/bash
echo "OLTP Test Started: $(date)"
# 10セッション同時にキック
for i in {1..10}
do
sqlplus -s user01/pass@Lin96:1521/orcl_pdb @oltp_test.sql &
done
wait
echo "OLTP Test Ended: $(date)"
それでは、早速ワークロードを実行してみましょう。
[oracle@Lin96 tmp]$ ./run_oltp.sh OLTP Test Started: 2026年 3月 2日 月曜日 11:21:53 JST PL/SQLプロシージャが正常に完了しました。 経過: 00:03:42.87 PL/SQLプロシージャが正常に完了しました。 経過: 00:03:43.17 PL/SQLプロシージャが正常に完了しました。 経過: 00:03:42.95 PL/SQLプロシージャが正常に完了しました。 経過: 00:03:43.22 PL/SQLプロシージャが正常に完了しました。 経過: 00:03:42.75 PL/SQLプロシージャが正常に完了しました。 経過: 00:03:42.77 PL/SQLプロシージャが正常に完了しました。 経過: 00:03:43.14 PL/SQLプロシージャが正常に完了しました。 経過: 00:03:43.47 PL/SQLプロシージャが正常に完了しました。 経過: 00:03:42.97 PL/SQLプロシージャが正常に完了しました。 経過: 00:03:43.06 OLTP Test Ended: 2026年 3月 2日 月曜日 11:25:38 JST
それぞれのセッションにおいて、1,000ループ完了までにおおよそ3分40秒程度(シェルスクリプトの完了までに3分45秒)要しました。
この処理の間、Oracle内部ではリフレッシュ処理を直列化するための待機イベントである「enq: JI - contention」が発生していました。
「enq: JI - contention」のパラメータ(P1列およびその16進数のP1RAW列)から、JI enqueueを排他モードで獲得することで、リフレッシュを直列化しMViewを保護していることが見受けられます。
SQL> select EVENT, count(*) from V$SESSION 2 where STATUS='ACTIVE' 3 and EVENT in (select NAME from V$EVENT_NAME where WAIT_CLASS != 'Idle') 4 group by EVENT order by 2 desc; EVENT COUNT(*) --------------------------------------------- ---------- enq: JI - contention 10 log file parallel write 1 SQL> select SID, EVENT, P1, P1RAW from V$SESSION 2 where EVENT like 'enq%' and STATE = 'WAITING'; SID EVENT P1 P1RAW ---------- --------------------------------------------- ---------- ---------------- 6 enq: JI - contention 1246298118 000000004A490006 17 enq: JI - contention 1246298118 000000004A490006 135 enq: JI - contention 1246298118 000000004A490006 389 enq: JI - contention 1246298118 000000004A490006 504 enq: JI - contention 1246298118 000000004A490006 510 enq: JI - contention 1246298118 000000004A490006 623 enq: JI - contention 1246298118 000000004A490006 752 enq: JI - contention 1246298118 000000004A490006 876 enq: JI - contention 1246298118 000000004A490006 9行が選択されました。
次の検証では、同時リフレッシュを有効にすることでどれだけスループットが改善するか、また「enq: JI - contention」が抑制されるかを観察してみたいと思います。
まず作成したMView「SALES_SUMMARY_MV」の同時リフレッシュを有効化します。
有効化するには「ALTER MATERIALIZED VIEW … ENABLE CONCURRENT REFRESH」コマンドを実行します。
また、MViewの作成タイミングに「CREATE MATERIALIZED VIEW」コマンドで同時リフレッシュを有効化することも可能です。
SQL> alter materialized view SALES_SUMMARY_MV ENABLE CONCURRENT REFRESH; マテリアライズド・ビューが変更されました。 SQL> select MVIEW_NAME,REFRESH_METHOD,REFRESH_MODE,CONCURRENT_REFRESH_ENABLED 2 from USER_MVIEWS; MVIEW_NAME REFRESH_METHOD REFRESH_MODE CONCURRENT_REFRESH_ENABLED ------------------------- ------------------------ --------------------------- ------------------------------ SALES_SUMMARY_MV FAST COMMIT Y
それでは、改めて同時リフレッシュ無効時と同じワークロードを、有効化後にも実行してみましょう。
[oracle@Lin96 tmp]$ ./run_oltp.sh OLTP Test Started: 2026年 3月 2日 月曜日 11:45:22 JST PL/SQLプロシージャが正常に完了しました。 経過: 00:02:06.21 PL/SQLプロシージャが正常に完了しました。 経過: 00:02:07.12 PL/SQLプロシージャが正常に完了しました。 経過: 00:02:07.30 PL/SQLプロシージャが正常に完了しました。 経過: 00:02:07.36 PL/SQLプロシージャが正常に完了しました。 経過: 00:02:07.91 PL/SQLプロシージャが正常に完了しました。 経過: 00:02:07.94 PL/SQLプロシージャが正常に完了しました。 経過: 00:02:07.92 PL/SQLプロシージャが正常に完了しました。 経過: 00:02:08.25 PL/SQLプロシージャが正常に完了しました。 経過: 00:02:08.14 PL/SQLプロシージャが正常に完了しました。 経過: 00:02:08.63 OLTP Test Ended: 2026年 3月 2日 月曜日 11:47:31 JST
1セッションあたり1,000ループ完了するまでの時間が、2分10秒ほど(シェルスクリプトの完了までに2分9秒)にまで短縮されました。
| 同時リフレッシュが無効 | 3分45秒 |
| 同時リフレッシュが有効 | 2分9秒 |
待機イベントの傾向にも変化があり、「enq: JI - contention」とは別のリソース保護でエンキュー獲得待ちが発生するタイミングがあるものの、ワークロード全体としては3分45秒 → 2分9秒と1分30秒以上のスループット改善が確認できました。
SQL> select EVENT, count(*) from V$SESSION 2 where STATUS='ACTIVE' 3 and EVENT in (select NAME from V$EVENT_NAME where WAIT_CLASS != 'Idle') 4 group by EVENT order by 2 desc; EVENT COUNT(*) --------------------------------------------- ---------- library cache lock 6 enq: JI - MV setup 2 library cache: mutex X 2 log file parallel write 1 SQL> select SID, EVENT, P1, P1RAW from V$SESSION 2 where EVENT like 'enq%' and STATE = 'WAITING'; SID EVENT P1 P1RAW ---------- --------------------------------------------- ---------- ---------------- 1 enq: JI - MV setup 1246298115 000000004A490003 6 enq: JI - MV setup 1246298116 000000004A490004 138 enq: JI - MV setup 1246298115 000000004A490003 264 enq: JI - MV setup 1246298115 000000004A490003 383 enq: JI - MV setup 1246298116 000000004A490004 510 enq: JI - MV setup 1246298116 000000004A490004 628 enq: JI - MV setup 1246298115 000000004A490003 750 enq: JI - MV setup 1246298115 000000004A490003 876 enq: JI - MV setup 1246298116 000000004A490004 9行が選択されました。
「enq : JI - MV setup」では、JI enqueueを行排他および共有モードで獲得しています。
ここから、同時リフレッシュが有効であれば「enq : JI - contention」のように表ごと排他ロックしないため、複数セッションからのリフレッシュを実現することができているように見受けられました。
次に、全国のスマートメーターから数分おきに届く電力データの、並列マイクロバッチ処理を想定した検証を行います。
各地域ごとの集約データを並列でマスター表(ファクト表)「SMART_METER_LOGS」に溜め込みます。
SQL> create TABLE SMART_METER_LOGS ( 2 LOG_ID number primary key, 3 AREA_CODE number, 4 METER_ID number, 5 KWH_VALUE number, 6 LOG_TIMESTAMP date 7 ); 表が作成されました。
このファクト表から、例えば電力ひっ迫アラートなどの監視システムが参照するサマリー表としてMViewを作成します。
地域(AREA_CODE)ごとの総電力消費量(SUM)を計算することで、この監視システムを実現します。
まずは、MView「AREA_POWER_SUMMARY_MV」を従来の同時リフレッシュが無効なON COMMIT MViewとして作成します。
SQL> create MATERIALIZED VIEW LOG on SMART_METER_LOGS 2 with ROWID, 3 SEQUENCE (AREA_CODE, KWH_VALUE, LOG_TIMESTAMP), 4 COMMIT SCN 5 including new values; マテリアライズド・ビュー・ログが作成されました。 SQL> create MATERIALIZED VIEW AREA_POWER_SUMMARY_MV 2 build immediate 3 REFRESH FAST ON COMMIT 4 as 5 select AREA_CODE, count(*) as LOG_COUNT, sum(KWH_VALUE) as TOTAL_KWH 6 from SMART_METER_LOGS 7 group by AREA_CODE; マテリアライズド・ビューが作成されました。
地域内のスマートメーター情報を集約した、まとまったデータの登録をイメージして、OLTPのケースよりも大きい次のようなトランザクションを実行します。
[oracle@Lin96 tmp]$ cat ./microbatch_test.sql set TIMING ON declare V_SID number; V_BASE_ID NUMBER; V_AREA_CODE NUMBER; begin -- 自身のセッションIDを取得 select SYS_CONTEXT('USERENV', 'SID') into V_SID from DUAL; -- セッションIDをAREA_CODEとして扱う V_AREA_CODE := V_SID; V_BASE_ID := V_SID * 10000000; -- 10回のマイクロバッチ (1回あたり5万件、計50万件/セッション) for i in 1..10 LOOP -- IoTデータを一括生成&INSERT insert into SMART_METER_LOGS (LOG_ID, AREA_CODE, METER_ID, KWH_VALUE, LOG_TIMESTAMP) select V_BASE_ID + (i * 1000000) + LEVEL, V_AREA_CODE, mod(LEVEL, 1000), round(DBMS_RANDOM.VALUE(1, 10), 2), SYSDATE from DUAL connect by LEVEL <= 50000; COMMIT; end LOOP; end; / exit;
この処理を全国10か所の地域からファクト表へとロードする状況を再現するために、10セッションから並行して処理を実行します。
[oracle@Lin96 tmp]$ cat ./run_microbatch.sh
#!/bin/bash
echo "Micro-batch Test Started: $(date)"
# 10か所の地域集約データを同時ロード
for i in {1..10}
do
sqlplus -s user01/pass@Lin96:1521/orcl_pdb @microbatch_test.sql &
done
wait
echo "Micro-batch Test Ended: $(date)"
こちらのワークロードを実行してみましょう。
[oracle@Lin96 tmp]$ ./run_microbatch.sh Micro-batch Test Started: 2026年 3月 2日 月曜日 15:31:38 JST PL/SQLプロシージャが正常に完了しました。 経過: 00:07:48.96 PL/SQLプロシージャが正常に完了しました。 経過: 00:08:01.51 PL/SQLプロシージャが正常に完了しました。 経過: 00:08:03.14 PL/SQLプロシージャが正常に完了しました。 経過: 00:08:04.85 PL/SQLプロシージャが正常に完了しました。 経過: 00:08:06.31 PL/SQLプロシージャが正常に完了しました。 経過: 00:08:07.09 PL/SQLプロシージャが正常に完了しました。 経過: 00:08:07.49 PL/SQLプロシージャが正常に完了しました。 経過: 00:08:08.83 PL/SQLプロシージャが正常に完了しました。 経過: 00:08:09.36 PL/SQLプロシージャが正常に完了しました。 経過: 00:08:10.05 Micro-batch Test Ended: 2026年 3月 2日 月曜日 15:39:49 JST
おおよそ8分前後で各セッションの処理が完了し、シェルスクリプトは8分11秒で完了しました。
今回のワークロードについては、SQLファイルmicrobatch_test.sqlのとおり、大規模な更新処理が伴うため、MViewのリフレッシュだけではなくI/Oにかかる処理時間も大部分を占めるかと思います。
ただし、次のようにリフレッシュのフェーズにおいてはやはり「enq: JI - contention」がボトルネックの一つとなってしまうようです。
SQL> select EVENT, count(*) from V$SESSION 2 where STATUS='ACTIVE' 3 and EVENT in (select NAME from V$EVENT_NAME where WAIT_CLASS != 'Idle') 4 group by EVENT order by 2 desc; EVENT COUNT(*) --------------------------------------------- ---------- enq: JI - contention 8 log buffer space 1 buffer busy waits 1 log file parallel write 1
それでは、同時リフレッシュを有効化した場合にマイクロバッチ処理のパフォーマンスはどのように変化するでしょうか。
まずは同時リフレッシュを有効化します。
SQL> alter materialized view AREA_POWER_SUMMARY_MV ENABLE CONCURRENT REFRESH; マテリアライズド・ビューが変更されました。 SQL> select MVIEW_NAME,REFRESH_METHOD,REFRESH_MODE,CONCURRENT_REFRESH_ENABLED 2 from USER_MVIEWS; MVIEW_NAME REFRESH_METHOD REFRESH_MODE CONCURRENT_REFRESH_ENABLED ------------------------- ------------------------ --------------------------- ------------------------------ AREA_POWER_SUMMARY_MV FAST COMMIT Y 1
同時リフレッシュを有効化したところで、同じワークロードを再実行します。
[oracle@Lin96 tmp]$ ./run_microbatch.sh Micro-batch Test Started: 2026年 3月 2日 月曜日 15:40:21 JST PL/SQLプロシージャが正常に完了しました。 経過: 00:05:32.09 PL/SQLプロシージャが正常に完了しました。 経過: 00:05:38.71 PL/SQLプロシージャが正常に完了しました。 経過: 00:05:44.31 PL/SQLプロシージャが正常に完了しました。 経過: 00:05:55.78 PL/SQLプロシージャが正常に完了しました。 経過: 00:05:56.88 PL/SQLプロシージャが正常に完了しました。 経過: 00:06:03.58 PL/SQLプロシージャが正常に完了しました。 経過: 00:06:04.62 PL/SQLプロシージャが正常に完了しました。 経過: 00:06:04.83 PL/SQLプロシージャが正常に完了しました。 経過: 00:06:11.08 PL/SQLプロシージャが正常に完了しました。 経過: 00:06:11.74 Micro-batch Test Ended: 2026年 3月 2日 月曜日 15:46:34 JST
なんと5分半から6分程度で処理が完了するセッションもあり、シェルスクリプト全体としては6分13秒の処理時間となりました。
同時リフレッシュの無効時の8分11秒と比較して1分58秒のスループット改善という、実運用上も十分に意味のある結果が得られました。
| 同時リフレッシュが無効 | 8分11秒 |
| 同時リフレッシュが有効 | 6分13秒 |
一方で、マイクロバッチ規模の大きなトランザクションでは、同時リフレッシュ有効時に「row cache lock」をはじめとした待機イベントが目立つ傾向も確認できました。
SQL> select EVENT, count(*) from V$SESSION 2 where STATUS='ACTIVE' 3 and EVENT in (select NAME from V$EVENT_NAME where WAIT_CLASS != 'Idle') 4 group by EVENT order by 2 desc; EVENT COUNT(*) --------------------------------------------- ---------- enq: JI - MV setup 4 row cache lock 4 latch: undo global data 1 log buffer space 1 log file parallel write 1 db file async I/O submit 1 6行が選択されました。
同時リフレッシュが無効な場合は、「enq: JI - contention」によってリフレッシュ処理そのものが直列化されました。
対して有効な場合には、Oracle AI Database内部のメタデータ更新時における排他制御(ディクショナリキャッシュの保護)フェーズで「row cache lock」などの待機が顕在化するかたちで、ボトルネックの位置が移動していると言えます。
こういった検証結果から、「どのような環境」「どのようなワークロード」においても同時リフレッシュの活用が最適な選択肢となるかは断言が難しいものと考えられます。
機能の採択に際しては、やはり本番相当のワークロードによるテストは必須と感じる検証結果となりました。
26aiの同時リフレッシュは、従来「enq: JI - contention」によって直列化されていたON COMMIT MViewのリフレッシュを並列化し、OLTP/DWHのいずれにおいてもスループット改善という明確なメリットをもたらします。
その一方で、今回の検証では大規模トランザクション時に「row cache lock」など別種のボトルネックが現れるケースも確認されました。
従って、本機能は「常に有効化しておけばよい」というより、システム特性やワークロードに応じて使いどころを見極めることで、より効果を引き出せるタイプの機能と言えます。
読者のみなさまが運用・保守するシステム特性が、本記事で取り上げたOLTP/DWHのどのパターンに近いかを意識しつつ、自システムのワークロードで挙動と効果をぜひ検証してみてください。
■本記事の内容について
本記事に記載されている製品およびサービス、定義及び条件は、特段の記載のない限り本記事執筆時点のものであり、予告なく変更になる可能性があります。あらかじめご了承ください。
■商標に関して
・Oracle®、Java及びMySQLは、Oracle、その子会社及び関連会社の米国及びその他の国における登録商標です。
・Amazon Web Services、AWS、Powered by AWS ロゴ、[およびかかる資料で使用されるその他の AWS 商標] は、Amazon.com, Inc. またはその関連会社の商標です。
文中の社名、商品名等は各社の商標または登録商標である場合があります。
BaseDBの運用でData Pump用の領域が不足した際、外部ストレージの活用が有効です。本記事では3つのストレージについて1TB利用時のコスト目安や性能、運用負荷を徹底比較します。自社環境に最適な外部ストレージ選びのポイントが分かります。
Oracle Trace File Analyzer(TFA)は、障害時のログ収集を効率化するツールです。複数ログの一括取得や時間指定、シングル環境での導入手順まで、現場目線でわかりやすく解説します。
本記事では、SYSAUX表領域の肥大化の主な原因と、現場DBAの方がいますぐ実践できる原因特定・対処・予防のステップを、具体的なSQL例とともに整理して解説します。