皆さんこんにちは、8月後半から急に涼しくなってどうなるのかと思っていたら、もう夏が終わり秋という感じになってしまいましたね(夏が早く終わってしまい少し寂しい気分ですね)。それにしても豪雨による被害が多いですので、皆さんも気を付けてください(被害にあった方は大変だと思いますが頑張ってください)。
今回もパラレル実行の続きとして、もう一つの難しい部分であるパラレル度について説明します。パラレル実行は性能向上に効果的ですが、パラレル度をどのようにするかが問題です。特に、多重実行を行うような場合には、パラレル実行にするかどうかも含めて悩むところです。そこで今回は、Oracle Database 12cで改善されている自動並列度やパラレル実行の注意点について説明しますので、参考にしてください。
1. パラレル度について
パラレル度は、多ければ速いという訳でもないので、最適なパラレル度にするにはある程度のテストが必要になります。また、多重実行するときは、PQプロセスが不足するとシリアル実行などになってしまうので、そのようなことも意識して決める必要があります。また、最適なパラレル度は、SQLごとに異なる場合もあるので、それを最適に設定するのには限界があります。このように、パラレル実行するためには検討することが多いので、効果的に使用している方は少ないのではないでしょうか。
これを自動的に決定できると非常に運用が楽になるので、Oracle Database 11gR2(Oracle11gR2)から自動並列度(自動DOP)が提供されましたが、これには限界があり使用できない場合が多かったです。そのため、Oracle Database 12c(Oracle12c)からパラレル度について少し拡張されているので、その中から以下について説明します。
- 自動並列度
- パラレル・ステートメント・キューイング
- LOBのパラレルDML
(1)自動並列度
まずは、Oracle12cの自動並列度について説明します。
自動並列度は、SQLによってパラレル度を自動的に調整する機能ですが、これまではアクセスするオブジェクト・サイズだけで決めていたので、パラレル度の決定があまり最適ではありませんでした。これは、オブジェクト・サイズとI/Oキャリブレーション統計から時間を見積もることで、以下のようにパラレル度の決定を行います。
- PARALLEL_MIN_TIME_THRESHOLD(デフォルト10秒)未満だったらシリアル実行する
- それ以外は見積もり時間からパラレル度を決定して実行する
パラレル実行では、I/O以外にCPU処理でも性能向上することができるので、以下のようにI/O時間の割合が小さいSQLの場合には、最適でない(小さい)パラレル度になってしまいました(例えば、Group ByのようなCPUを使用する処理が多いSQLなどです)。

そのため、Oracle12cからはCPU時間も考慮されるようになっているので、このようなSQLでも最適なパラレル度(または最適に近いパラレル度)が設定されるようになります。
この機能を使用したときのもう一つの利点は、SQLごとに最適なパラレル度が設定されるので、無駄なPQプロセスの使用を削減することが可能になることです。すべてのSQLでテストをして、最適なパラレル度をヒント文などで設定するのは大変な作業になるので、このような作業を行わずに設定できるのはとても嬉しいことです。
(2)パラレル・ステートメント・キューイング
次に、パラレル・ステートメント・キューにングについて説明します。
自動並列度が効果的に使用できるようになったとしても、PQプロセスが不足するとシリアル実行になってしまいます。そのため、パラレル・ステートメント・キューイングと併用することでさらに効果的になります(これは第20回でも説明したように、メモリを多く使用するSQLなどはパラレルで実行した方が効果的だからです)。ただし、優先度の割合(リソース・マネージャのmgmt_p1パラメータ)などでデキューを調整することは可能ですが、重要なSQLなどをキューイングさせないようにはできないので、使用するのが難しい場合もありました。そのため、Oracle12cから以下のように、リソース・マネージャのPARALLEL_STMT_CRITICALパラメータを’BYPASS_QUEUE’に設定することで(デフォルトはNULLです)、クリティカルとしてコンシューマ・グループが作成され、キューイングを回避してすぐに動作することが可能になります。

このようなSQLが存在するときでも使いやすくなりますが、以下のようにあまり多くすると意味がなくなるので、クリティカルにするSQLの決定には注意するようにしてください。
- 初期化パラメータPARALLEL_SERVERS_TARGETよりも多くのPQプロセスが動作する場合がある
- PQプロセスが初期化パラメータPARALLEL_MAX_SERVERSに達すると、一部のクリティカルなSQLのパラレル度がダウングレードされる場合がある
パラレル・ステートメント・キューイングのリソース管理について
ご存知ない方のために、ここでリソース・マネージャによるパラレル・ステートメント・キューイングの管理について簡単に説明します。
パラレル・ステートメント・キューイングは、第20回で少し説明したように、パラレル実行で使用するPQプロセスが不足してもシリアル実行するのではなく、PQプロセスが使用できるまでキューイングしてパラレル実行する機能です。シリアル実行よりもパラレル実行した方が最適な場合もあるので、勝手にシリアル実行させたくない場合に便利な機能になります。リソース・マネージャと一緒に使用することで、リソース・コンシューマ・グループ(コンシューマ・グループ)ごとにキューを管理することができるようになり、より柔軟に管理することが可能になります。このコンシューマ・グループに対して、以下のパラメータで調整することが可能です。
- デキュー順序(mgmt_p1~mgmt_p8パラメータ)
デキューは、基本はFIFOで動作しますが、この優先度の割合を指定することで、コンシューマ・グループごとにデキューする順番を調整します(この割合でそれぞれのコンシューマ・グループをデキューします)。 - PQプロセス・リソースの制限(parallel_target_percentageパラメータ、Oracle12cからparallel_server_limitパラメータ)
キューイングは、初期化パラメータPARALLEL_SERVERS_TARGETで制御しますが、このパラメータ(PARALLEL_SERVERS_TARGETに対する割合)を指定することで、コンシューマ・グループごとに制御します。特定のコンシューマ・グループだけPQプロセスを多く使用するのを防ぎます。 - タイムアウト(parallel_queue_timeoutパラメータ)
コンシューマ・グループのキューに留まることのできる時間を秒単位で指定します。SQLは、この時間が経過するとORA-7454エラーとなり、キューから削除されます。キューイングによって応答時間が遅くなるのを防ぎます。 - パラレル度制限(parallel_degree_limit_p1パラメータ)
コンシューマ・グループのパラレル度の最大値を指定します。これは、PQプロセス数を少なくすることで、間接的にキューイングのタイミングを制御します。
(3)LOBのパラレルDML
最後に、パラレル度とは少し異なりますが、Oracle12cから拡張されたLOBのパラレルDMLについて説明します。
LOBのパラレル実行は、第28回では説明していませんでしたが、Oracle11gR2でもパラレル実行することは可能でした。ただし、パラレルDMLについては、パーティション化しているテーブルだけが可能だったので、複数パーティションにアクセスしないとパラレル実行できないという、Oracle9iまでの通常のパラレルDMLと同じような制限がありました。この制限によって、あまりパラレル実行することができなかったのではないでしょうか。Oracle12cからSecureFiles LOBだけ制限がなくなって、非パーティション表(またはパーティション内)でもパラレルDMLを行うことが可能になったので、よりパラレルDMLを行うことが簡単になっています。
以下の非パーティション表’tab1’とパーティション表’tab2’に、パラレルDMLを行って実行計画を確認してみます。
SQL> CREATE TABLE tab1 (c1 NUMBER, a BLOB) LOB (a) STORE AS SECUREFILE TABLESPACE users; SQL> CREATE TABLE tab2 (c1 NUMBER, a BLOB) LOB (a) STORE AS SECUREFILE TABLESPACE users 2 PARTITION BY HASH (c1) PARTITIONS 4;
以下が非パーティション表’tab1’に対して、Oracle11gR2でパラレルDMLを行った場合の実行計画です(分かりやすいように同じテーブルにSELECTとINSERTを行っています)。’PX COORDINATOR’の後で(コーディネータ・プロセスが受信してから)’LOAD AS SELECT’を行っていることから、ダイレクト・パス・インサートがシリアル処理で行っているのが分かります(シリアルINSERTのデフォルトが非ダイレクト・パス・インサートなので、ダイレクト・パス・インサートするためにAPPENDヒントを使用しています)。
————————————————————————-
| Id | Operation | Name | | TQ |IN-OUT| PQ Distrib |
————————————————————————-
| 0 | INSERT STATEMENT | | | | | |
| 1 | LOAD AS SELECT | | | | | |
| 2 | PX COORDINATOR | | | | | |
| 3 | PX SEND QC (RANDOM)| :TQ10000 | | Q1,00 | P->S | QC (RAND) |
| 4 | PX BLOCK ITERATOR | | | Q1,00 | PCWC | |
|* 5 | TABLE ACCESS FULL| TAB1 | | Q1,00 | PCWP | |
以下がパーティション表’tab2’に対して、Oracle11gR2でパラレルDMLを行った場合の実行計画です。’PX SEND PARTITION (KEY)’でパーティション・キーによって分割することでパラレル実行を行っています。’LOAD AS SELECT’が’PX SEND QC (RANDOM)’の前で行っていることから、パラレル実行で行っているのが分かります。
———————————————————————————————–
| Id | Operation | Name | | Pstart| Pstop | TQ |IN-OUT| PQ Distrib |
———————————————————————————————–
| 0 | INSERT STATEMENT | | | | | | | |
| 1 | PX COORDINATOR | | | | | | | |
| 2 | PX SEND QC (RANDOM) | :TQ10001 | | | | Q1,01 | P->S | QC (RAND) |
| 3 | LOAD AS SELECT | | | | | Q1,01 | PCWP | |
| 4 | PX RECEIVE | | | | | Q1,01 | PCWP | |
| 5 | PX SEND PARTITION (KEY)| :TQ10000 | | | | Q1,00 | P->P | PART (KEY) |
| 6 | PX BLOCK ITERATOR | | | 1 | 4 | Q1,00 | PCWC | |
|* 7 | TABLE ACCESS FULL | TAB2 | | 1 | 4 | Q1,00 | PCWP | |
以下が非パーティション表’tab1’に対して、Oracle12cでパラレルDMLを行った場合の実行計画です。これも’LOAD AS SELECT’が’PX SEND QC (RANDOM)’の前で行っているので、パラレル実行で行っています。そして、データ分散を行っていないので(’TABLE ACCESS FULL’と’LOAD AS SELECT’の間に’PX SEND XXXX’がないので)、少し効果的に動作することが可能になります。

2. パラレル実行時の注意点
パラレル実行(特にパラレルDML)するには、少し注意が必要な部分があります。そのため、パラレルで動作しない、または遅くなってしまう場合もあるので、注意点として以下のようなことを少しまとめてみました。
- パラレルDMLのロック
- トリガー及び制約
- パラレル度の決定
(1)パラレルDMLのロック
まずは、パラレルDMLのロックについて説明します。
ロックについては、第18回で説明したようにOracleデータベースは行ロックのため競合が少ないですが、DDLやLOCK TABLE文以外にもテーブルの排他ロック(TMエンキューのXモード)を取得する場合があります。その代表的なSQLがダイレクト・パス・インサートで、実行中は他のセッションでアクセスすることができなくなります(これについては、ご存知の方も多いと思います)。これは、パラレルDMLでも同じように表ロックされるので注意してください。つまり、多重ユーザで実行する可能性があるテーブルに、パラレルDMLを行ってはいけないということです。これは、単体テストでは分からないことなので、システムテストなどで後戻りにならないように、これも知っておいた方が良いことです。以下のパラレルDML(UPDATE)の例を使用してロックを確認してみます。
SQL> ALTER SESSION FORCE PARALLEL DML PARALLEL 2; SQL> UPDATE t3 SET col2 = 'AA' WHERE col1 = 100;
この場合のロック情報’v$lock’は以下のようになります。パラレルDMLでもダイレクト・パス・インサートのように、テーブルに対して排他ロックが取得されています(TYがロックのタイプ、LMODEが6のとき排他ロックです)。

そうはいっても大量に更新を行うような場合には、実行時間が長くなってしまうので、悩むところではあると思います。そのため、ロック待ちが発生してもパラレル実行で短時間に終了させるのも一つの方法になるので、どちらが効果的かをよく考えて決定するのが良いと思います(例えば、バッチ処理実行時には、ほとんどオンライン処理が行われないので、影響が少ないからパラレル実行しても問題ない場合などです)。
(2)トリガー及び制約
次に、トリガー及び制約について説明します。
パラレルDMLは、トリガーや制約が存在するテーブルでは動作しない場合があります。第20回で説明した同一トランザクション内で同一テーブルにアクセスできない以外に、以下のような場合にもパラレルDMLは動作しません。このようなテーブルに対しては、パラレル実行するためには外してから実行する必要があります。これも知っておくとテスト時に原因不明の性能問題などで悩まずに済みます。テーブル設計者とプログラム作成者が異なる場合などに、プログラム作成者が知らない場合も多いと思うので注意してください。
- トリガーはパラレルDMLでは動作しない。
- 自己参照型整合性、削除カスケードおよび遅延制約(SET CONSTRAINT … DEFERRED)など、特定の制約がある場合はパラレルDMLは実行できない。
- ダイレクト・パス・インサートの場合、参照整合性はサポートされない。
(3)パラレル度の決定
最後に、パラレル度を決定するルールについて説明します。
これも間違いやすいので、知っておいた方が良いと思い、参考レベルで載せておきます。以下の順番でパラレル度が決定されるので、どれが優先するか間違わないようにパラレル度を設定してください。つまり、ALTER SESSIONのパラレル度が最も優先度が高いので、これでパラレル度を上書きすることが可能です。そのため、パラレル度のテストなどには便利です。
- セッションのパラレル度(ALTER SESSION FORCE PARALLEL QUERY PARALLEL <パラレル度>)
- PARALLELヒント
- PARALLEL_DEGREE_POLICY(自動並列度)
- テーブルのパラレル句
4. おわりに
今回はパラレル実行のパラレル度についていろいろ説明しましたが、少しは参考になりましたでしょうか。また機会があれば他のことについても説明したいと思います。これからもよろしくお願いします。
それでは、次回まで、ごきげんよう。
