はじめに

こんにちは。Oracle Databaseの検証チームです。
今回は、Oracle Database 26aiの新機能である「マテリアライズド・ビューの同時リフレッシュ」をご紹介いたします。
※本機能は従来の23aiから使用可能です。

今回の執筆者は以下の記事に登場しています!
ORACLE MASTER Silver DBA 2019、 Oracle Cloud Infrastructure 2024 Certified Foundations Associateを取得しました!

過去のOracle Database 26aiの記事はこちら
一部23aiと表示されています。

マテリアライズド・ビューとは

マテリアライズド・ビューとは、事前に定義されたSELECT結果を表として保持する機能です。

ビューに対し、実テーブルにSELECT結果を保持しているため比較的高速にデータの取得ができます。
また、実テーブルに保持しているデータはある時点のものであるため、最新化する場合は別途リフレッシュが必要となります。

マテリアライズド・ビューの同時リフレッシュとは

従来のマテリアライズド・ビューでは、on commit時のリフレッシュ処理はシリアルでの実行となっており、リフレッシュを行ったセッションが終了するまでその他のリフレッシュはブロックされていました。

Oracle Database 26aiからは、マテリアライズド・ビューを複数のセッションから同時にリフレッシュすることができるようになりました。

また、マテリアライズド・ビューの同時リフレッシュを利用するには条件があり、Oracleのマニュアルでは以下のように紹介されています。
マテリアライズド・ビューのリフレッシュ

■ コミット時のマテリアライズド・ビューの同時リフレッシュが許可される条件次の条件によって、同時リフレッシュを続行できるかどうかが決まります。
・同時リフレッシュが有効になっている。(有効にできるのは、コミット時のマテリアライズド・ビューのリフレッシュのみです。)
・すべての同時DMLセッションで同じ実表が更新される。
・異なるリフレッシュ・セッションで更新されたマテリアライズド・ビュー行が重複しない。

検証

※本検証では、Oracle VM VirtualBoxにOracle Database 23ai Free Developer をインストールした環境を使用しています。

準備

本検証用では、以下のテーブルとマテリアライズド・ビューを作成します。
それぞれのマテリアライズド・ビューは共通のディテール表(表1,表2)を使用します。

表1:orders

【サンプルDDL】
CREATE TABLE orders (
 id NUMBER(10),
 created_date DATE,
 CONSTRAINT orders_pk PRIMARY KEY (id)
);
CREATE MATERIALIZED VIEW LOG ON FRUIT_TAB WITH ROWID,PRIMARY KEY;
※差分リフレッシュを行えるようにマテリアライズド・ビューログも必要
ID CREATED_DATE
1 25 - 08 - xx
2 xx - xx - xx
・・・ ・・・

表2:order_lines

【サンプルDDL】
CREATE TABLE order_lines (
 id NUMBER(10),
 order_id NUMBER(10),
   line_qty NUMBER(5),
 total_value NUMBER(10,2),
 created_date DATE,
   CONSTRAINT order_lines_pk PRIMARY KEY (id),
   CONSTRAINT ol_o_fk FOREIGN KEY (order_id) REFERENCES orders(id)
);

CREATE MATERIALIZED VIEW LOG ON order_lines
WITH ROWID,PRIMARY KEY, SEQUENCE(order_id, line_qty, total_value) INCLUDING NEW VALUES;
※差分リフレッシュを行えるようにマテリアライズド・ビューログも必要
ID ORDER_ID LINE_QTY TOTAL_VALUE CREATED_DATE
1 34 xx xxx.xx 25 - 04 - xx
2 11 xx xxx.xx xx - xx - xx
・・・ ・・・ ・・・ ・・・ ・・・

マテリアライズド・ビュー1:mv_order_summary_serial(同時リフレッシュ無効)

【サンプルDDL】
CREATE MATERIALIZED VIEW mv_order_summary_serial
 REFRESH FAST ON COMMIT
 AS
 SELECT o.id AS order_id, COUNT(ol.id) AS line_count, SUM(ol.total_value) AS total_value
 FROM orders o
 JOIN order_lines ol ON o.id = ol.order_id
 GROUP BY o.id
;
ORDER_ID LINE_COUNT TOTAL_VALUE
1 1024 xxxxxx.xx
2 1012 xxxxxx.xx
3 1048 xxxxxx.xx

マテリアライズド・ビュー2:mv_order_summary_concurrent(同時リフレッシュ有効)

【サンプルDDL】
CREATE MATERIALIZED VIEW mv_order_summary_concurrent
 REFRESH FAST ON COMMIT ENABLE CONCURRENT REFRESH
 AS
 SELECT o.id AS order_id, COUNT(ol.id) AS line_count, SUM(ol.total_value) AS total_value
 FROM orders o
 JOIN order_lines ol ON o.id = ol.order_id
 GROUP BY o.id
;
ORDER_ID LINE_COUNT TOTAL_VALUE
1 1024 xxxxxx.xx
2 1012 xxxxxx.xx
3 1048 xxxxxx.xx

リフレッシュの情報を確認できるように、リフレッシュ統計収集を有効化しておきましょう。

SQL> EXEC DBMS_MVIEW_STATS.SET_SYSTEM_DEFAULT ('COLLECTION_LEVEL','ADVANCED');

PL/SQLプロシージャが正常に完了しました。

同時リフレッシュの有効化

同時リフレッシュが有効かどうかはALL_MVIEWSの「CONCURRENT_REFRESH_ENABLED」という項目で確認できます。

SQL> SELECT MVIEW_NAME,CONCURRENT_REFRESH_ENABLED from ALL_MVIEWS ;

MVIEW_NAME                    CONCURRENT_REFRESH_ENABLED 
____________________________  ___________________
MV_ORDER_SUMMARY_SERIAL       N 
MV_ORDER_SUMMARY_CONCURRENT   Y

「MV_ORDER_SUMMARY_SERIAL」のCONCURRENT_REFRESH_ENABLEDの値は「N」となっているため、同時リフレッシュが無効化されている状態です。
この状態だと従来通りシリアル実行のリフレッシュ動作となります。
もう一方の「MV_ORDER_SUMMARY_CONCURRENT」のCONCURRENT_REFRESH_ENABLEDの値は「Y」となっているため、同時リフレッシュが有効化されています。

後から有効化する場合は以下のSQLで有効化できます。

ALTER MATERIALIZED VIEW <マテリアライズド・ビュー名> ENABLE CONCURRENT REFRESH;

リフレッシュの確認

複数のセッションでINSERT,COMMITを行い、どのような差が出るか見てみましょう。

セッション1

SQL> SELECT SYS_CONTEXT('USERENV', 'SESSIONID');

SYS_CONTEXT('USERENV','SESSIONID') 
_____________________________________ 
120039

SQL> INSERT INTO orders
2 SELECT ID + 10000, TRUNC(SYSDATE - DBMS_RANDOM.VALUE(0, 366))
3* FROM orders WHERE ID <= 10000;

10,000行挿入しました。

SQL> INSERT INTO order_lines
2 SELECT ID + 1000000, TRUNC(DBMS_RANDOM.VALUE(1, 1000)), TRUNC(DBMS_RANDOM.VALUE(1, 20)),
3 ROUND(DBMS_RANDOM.VALUE(1, 1000), 2), TRUNC(SYSDATE - DBMS_RANDOM.VALUE(0, 366))
4* FROM order_lines WHERE ID <= 1000000;

1,000,000行挿入しました。

セッション2

SQL> SELECT SYS_CONTEXT('USERENV', 'SESSIONID');

SYS_CONTEXT('USERENV','SESSIONID') 
_____________________________________ 
120040

SQL> INSERT INTO orders
2 SELECT ID + 20000, TRUNC(SYSDATE - DBMS_RANDOM.VALUE(0, 366))
3* FROM orders WHERE ID <= 10000;

10,000行挿入しました。

SQL> INSERT INTO order_lines
2 SELECT ID + 2000000, TRUNC(DBMS_RANDOM.VALUE(1, 1000)), TRUNC(DBMS_RANDOM.VALUE(1, 20)),
3 ROUND(DBMS_RANDOM.VALUE(1, 1000), 2), TRUNC(SYSDATE - DBMS_RANDOM.VALUE(0, 366))
4* FROM order_lines WHERE ID <= 1000000;

1,000,000行挿入しました。

セッション3

SQL> SELECT SYS_CONTEXT('USERENV', 'SESSIONID');

SYS_CONTEXT('USERENV','SESSIONID') 
_____________________________________ 
120041

SQL> INSERT INTO orders
2 SELECT ID + 30000, TRUNC(SYSDATE - DBMS_RANDOM.VALUE(0, 366))
3* FROM orders WHERE ID <= 10000;

10,000行挿入しました。

SQL> INSERT INTO order_lines
2 SELECT ID + 3000000, TRUNC(DBMS_RANDOM.VALUE(1, 1000)), TRUNC(DBMS_RANDOM.VALUE(1, 20)),
3 ROUND(DBMS_RANDOM.VALUE(1, 1000), 2), TRUNC(SYSDATE - DBMS_RANDOM.VALUE(0, 366))
4* FROM order_lines WHERE ID <= 1000000;

1,000,000行挿入しました。

上記それぞれのセッションでCOMMITを発行し、リフレッシュの情報を確認してみます。

パターン1:マテリアライズド・ビューごとに確認

それぞれのマテリアライズド・ビューごとにリフレッシュの動作を確認したところ以下のようになりました。

【同時リフレッシュOFF】
SQL> SELECT 
2 MV_NAME, 
3 REFRESH_ID, 
4 REFRESH_METHOD, 
5 START_TIME, 
6 END_TIME, 
7 (END_TIME - START_TIME) AS ELAPSED_SECONDS 
8 FROM 
9 DBA_MVREF_STATS 
10 ORDER BY 
11* START_TIME DESC;

MV_NAME                     REFRESH_ID     REFRESH_METHOD     START_TIME                      END_TIME                        ELAPSED_SECONDS 
__________________________  _____________  _________________  ______________________________  ______________________________  ______________________ 
MV_ORDER_SUMMARY_SERIAL     26             FAST               25-10-23 11:15:02.000000000     25-10-23 11:15:15.000000000 +00 00:00:13.000000 
MV_ORDER_SUMMARY_SERIAL     25             FAST               25-10-23 11:08:35.000000000     25-10-23 11:09:15.000000000 +00 00:00:40.000000 
MV_ORDER_SUMMARY_SERIAL     24             FAST               25-10-23 11:03:29.000000000     25-10-23 11:04:14.000000000 +00 00:00:45.000000
【同時リフレッシュON】
SQL> SELECT 
2 MV_NAME, 
3 REFRESH_ID, 
4 REFRESH_METHOD, 
5 START_TIME, 
6 END_TIME, 
7 (END_TIME - START_TIME) AS ELAPSED_SECONDS 
8 FROM 
9 DBA_MVREF_STATS 
10 ORDER BY 
11* START_TIME DESC;

MV_NAME                         REFRESH_ID     REFRESH_METHOD     START_TIME                      END_TIME                        ELAPSED_SECONDS 
______________________________  _____________  _________________  ______________________________  ______________________________  ______________________ 
MV_ORDER_SUMMARY_CONCURRENT     29             FAST               25-10-23 11:33:30.000000000     25-10-23 11:44:11.000000000 +00 00:10:41.000000 
MV_ORDER_SUMMARY_CONCURRENT     28             FAST               25-10-23 11:33:30.000000000     25-10-23 11:39:01.000000000 +00 00:05:31.000000 
MV_ORDER_SUMMARY_CONCURRENT     27             FAST               25-10-23 11:33:30.000000000     25-10-23 11:34:20.000000000 +00 00:00:50.000000

START_TIMEとEND_TIMEの列を見てみると、同時リフレッシュがOFFの場合はシリアル実行されているのに対し、同時リフレッシュがONの場合は同じタイミングで開始されていることがわかります。
また、全体の実行時間に関しては同時リフレッシュOFFが12分弱、同時リフレッシュONが11分弱となっており、同時リフレッシュONの方が少し早いという結果になりました。

パターン2:同時に確認

同じディテール表に対して両パターンのマテリアライズド・ビューを作成した場合、以下のようになりました。

SQL> SELECT 
2 MV_NAME, 
3 REFRESH_ID, 
4 REFRESH_METHOD, 
5 START_TIME, 
6 END_TIME, 
7 (END_TIME - START_TIME) AS ELAPSED_SECONDS 
8 FROM 
9 DBA_MVREF_STATS 
10 ORDER BY 
11* START_TIME DESC; 

MV_NAME                         REFRESH_ID     REFRESH_METHOD     START_TIME                      END_TIME                        ELAPSED_SECONDS 
______________________________  _____________  _________________  ______________________________  ______________________________  ______________________ 
MV_ORDER_SUMMARY_SERIAL         23             FAST               25-10-23 08:55:15.000000000     25-10-23 08:55:30.000000000 +00 00:00:15.000000 
MV_ORDER_SUMMARY_CONCURRENT     23             FAST               25-10-23 08:55:15.000000000     25-10-23 08:55:38.000000000 +00 00:00:23.000000 
MV_ORDER_SUMMARY_SERIAL         22             FAST               25-10-23 08:49:26.000000000     25-10-23 08:49:51.000000000 +00 00:00:25.000000 
MV_ORDER_SUMMARY_CONCURRENT     22             FAST               25-10-23 08:49:26.000000000     25-10-23 08:50:10.000000000 +00 00:00:44.000000 
MV_ORDER_SUMMARY_SERIAL         21             FAST               25-10-23 08:47:31.000000000     25-10-23 08:47:57.000000000 +00 00:00:26.000000 
MV_ORDER_SUMMARY_CONCURRENT     21             FAST               25-10-23 08:47:31.000000000     25-10-23 08:48:06.000000000 +00 00:00:35.000000 

6行が選択されました。

START_TIMEの列を見ると、同時リフレッシュが有効な場合でもシリアル実行されていることがわかります。
また、同じ実行タイミングのリフレッシュを比較した場合、同時リフレッシュが有効になっている方が処理に時間がかかっているようです。
同時リフレッシュがONの場合でもシリアル実行となってしまう場合、従来のリフレッシュに戻したほうがよいかもしれませんね。

おわりに

今回はマテリアライズド・ビューの同時リフレッシュについて紹介してみました。

本機能はすべての場合に有効というわけではないですが、選択肢の幅が広がることでより便利になったと思います。
on commitを使用したマテリアライズド・ビューをお使いの環境で、処理速度が気になっている場合、26aiにて本機能を検討してみてはいかがでしょうか。

最後までお読みいただきありがとうございました。

Oracle Databaseに関してお困りごとがあれば、ぜひ当社へご相談ください。

Oracleの課題、専門家が解決します


Oracle導入を相談する

豊富な実績で最適な解決策をご提供

 

投稿者プロフィール

技術チーム
技術チーム
DBひとりでできるもんを盛り上げるべく、技術チームが立ち上がり早8年。ひとりでできるもんと言いつつ、技術者が読んでプッとなるような、極めてピンポイントでマニアックな技術ネタを執筆しています!
最新技術情報や資格情報をチェックしたいアナタ!毎日遊びに来てください。きっとお役に立てます。