Upgrade to Pro — share decks privately, control downloads, hide ads and more …

【Oracle Developer Day 2026】SQL性能改善の武器としてのパラレル実...

Avatar for oracle4engineer oracle4engineer PRO
May 22, 2026
170

【Oracle Developer Day 2026】SQL性能改善の武器としてのパラレル実行:実行計画で見抜く“効く/効かない”パターン

2026/05/21
[T2-3] SQL性能改善の武器としてのパラレル実行
- 実行計画で見抜く“効く/効かない”パターン
Oracle DatabaseのSQL性能改善における強力な武器であるパラレル実行を、実務目線で踏み込んで解説します。パラレル実行を用いると、多くのケースでSQLを書き換えずに、ヒントや設定の付与だけで、処理を並列化でき、大きな性能改善が期待できます。一方で、より確実に効果を引き出すには、データの分散(distribution)やデータの偏り(skew)、TEMP使用といった特性を理解し、状況に応じて設計・調整することが重要です。
本セッションでは、パラレル実行の内部動作を整理したうえで、効果が出にくいケースに遭遇した際も、実行計画などから状況を読み解き、どう手を打つかを紹介します。

辻 研一郎
日本オラクル株式会社

Avatar for oracle4engineer

oracle4engineer PRO

May 22, 2026

More Decks by oracle4engineer

Transcript

  1. 辻 研⼀郎 (Kenichiro Tsuji) Kenichiro Tsuji Principal Cloud Engineer 2

    Copyright © 2026, Oracle and/or its affiliates ⽇本オラクル株式会社 製品事業統括 AI Data Platform COE本部 データベース・ソリューション部 プリンシパル クラウド エンジニア
  2. アジェンダ 3 Copyright © 2026, Oracle and/or its affiliates 1.

    SQLのパラレル実⾏のイメージをつかむ 2. パラレル実⾏の課題1︓データ・スキュー 3. パラレル実⾏の課題2︓パラレル間データ通信 4. まとめ
  3. OracleのSQLのパラレル実⾏とは 5 Copyright © 2026, Oracle and/or its affiliates SQLのパラレル実⾏は、SQL⽂の内容はそのままで、複数プロセス(複数CPU)で実⾏して⾼速化する⽅法

    次のような場⾯ではパラレル実⾏が活躍できます ・⼤量データ処理 ・多重度が低い(同時実⾏SQLが少ない)状況 ▶バッチ処理やDWH なぜ、今パラレル実⾏なのか︖ ⼗数年前︓CPU数に対してストレージ能⼒が⾜らず、IOネックになる状況が多い。パラレル度をあげても性能が上がらない 2026年現在︓IOネックを解消する⽅法(フラッシュストレージ、Exadata、Autonomous DBなど) の登場でパラレル実⾏が真価を発揮しやすくなった このセッションでは、IOネックを超えた先でパラレル実⾏に真価を発揮させるときにぶつかるデータ・スキュー(データの偏り)、 パラレル間データ通信に踏み込んでいきます
  4. OracleのSQLのパラレル実⾏の基本イメージ 6 Copyright © 2026, Oracle and/or its affiliates パラレル実⾏のイメージを掴む

    こちらはシリアル実⾏の実⾏計画です、これをパラレル度4でパラレル実⾏するとどうなると思いますか︖ SQL> SELECT c1, count(*) cnt FROM tab02 2 GROUP BY c1 ORDER BY cnt; 実行計画 -------------------------------------- Plan hash value: 774632932 -------------------------------------- | Id | Operation | Name | -------------------------------------- | 0 | SELECT STATEMENT | | | 1 | SORT ORDER BY | | | 2 | HASH GROUP BY | | | 3 | TABLE ACCESS FULL| TAB02 |
  5. OracleのSQLのパラレル実⾏の基本イメージ 7 Copyright © 2026, Oracle and/or its affiliates パラレル実⾏のイメージを掴む

    こちらはシリアル実⾏の実⾏計画です、これをパラレル度4でパラレル実⾏するとどうなると思いますか︖ SQL> SELECT c1, count(*) cnt FROM tab02 2 GROUP BY c1 ORDER BY cnt; 実行計画 -------------------------------------- Plan hash value: 774632932 -------------------------------------- | Id | Operation | Name | -------------------------------------- | 0 | SELECT STATEMENT | | | 1 | SORT ORDER BY | | | 2 | HASH GROUP BY | | | 3 | TABLE ACCESS FULL| TAB02 | ◀パラレル度4 ◀パラレル度4 ◀パラレル度4 考え⽅1 実⾏計画のそれぞれのオペレーションをパラレル実⾏する(イントラ・オペレーション並列化) PX スキャン 表 イントラ・オペレーション並列化 PX PX PX GroupBy PX PX PX PX QC PX PX PX PX OrderBy それぞれのオペレーションをイントラ・オ ペレーションでパラレル実⾏する単位を PXサーバーセット(PXセット) といいます PXセット0 PXセット1 PXセット2
  6. OracleのSQLのパラレル実⾏の基本イメージ 8 Copyright © 2026, Oracle and/or its affiliates パラレル実⾏のイメージを掴む

    これはシリアル実⾏の実⾏計画です、これをパラレル度4でパラレル実⾏するとどうなると思いますか︖ SQL> SELECT c1, count(*) cnt FROM tab02 2 GROUP BY c1 ORDER BY cnt; 実行計画 -------------------------------------- Plan hash value: 774632932 -------------------------------------- | Id | Operation | Name | -------------------------------------- | 0 | SELECT STATEMENT | | | 1 | SORT ORDER BY | | | 2 | HASH GROUP BY | | | 3 | TABLE ACCESS FULL| TAB02 | ◀パラレル度4 ◀パラレル度4 ◀パラレル度4 考え⽅1 実⾏計画のそれぞれのオペレーションをパラレル実⾏する(イントラ・オペレーション並列化) PX スキャン 表 イントラ・オペレーション並列化 PX PX PX GroupBy PX PX PX PX QC PX PX PX PX OrderBy 考え⽅2 オペレーションをまたがってパラレル実⾏する(インター・オペレーション並列化) ▶両⽅発⽣します ①スキャンしながら GroupBy ②GroupByしながら OrderBy ! " # $ % & ' ( $ ) * " 並 列 化 ① ② QCごとにPXセットは最 ⼤2セット同時実⾏しま す。つまり、最⼤でパラレ ル度×2のプロセス数での 実⾏となります 前のPXセットの結果を⼊⼒として、次のPXセットが処理していくモ デルをプロデューサ/コンシューマ・モデルと呼びます
  7. パラレル実⾏の動作イメージを実⾏計画からつかむ 9 Copyright © 2026, Oracle and/or its affiliates ------------------------------------------------------------------------------------

    | Id | Operation | Name | TQ | IN-OUT | PQ Distrib | ------------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | | | | | 1 | PX COORDINATOR | | | | | | 2 | PX SEND QC (RANDOM) |:TQ10001| Q1,01 | P->S | QC (RAND) | | 3 | HASH GROUP BY | | Q1,01 | PCWP | | | 4 | PX RECEIVE | | Q1,01 | PCWP | | | 5 | PX SEND HASH |:TQ10000| Q1,00 | P->P | HASH | | 6 | HASH GROUP BY | | Q1,00 | PCWP | | | 7 | PX BLOCK ITERATOR | | Q1,00 | PCWC | | | 8 | TABLE ACCESS FULL| EMP | Q1,00 | PCWP | | ------------------------------------------------------------------------------------ 実⾏計画にあるパラレル実⾏独⾃のPX xxx、TQxxxx、TQ、IN-OUT、PQ Distribを利⽤して、PXセッ トから次のPXセットへデータを渡す流れのイメージをつかみます。 要素が多いですが、P→Pに注⽬するところから始めると楽です SELECT prod_id,SUM(amount_sold) FROM sales GROUP BY prod_id;
  8. パラレル実⾏の動作イメージを実⾏計画からつかむ 10 Copyright © 2026, Oracle and/or its affiliates -------------------------------------------------------------------------

    | Id | Operation | Name | TQ |IN-OUT| PQ Distrib | ------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | | | 1 | PX COORDINATOR | | | | | | 2 | PX SEND QC (RANDOM) | :TQ10001 | Q1,01 | P->S | QC (RAND) | | 3 | HASH GROUP BY | | Q1,01 | PCWP | | | 4 | PX RECEIVE | | Q1,01 | PCWP | | | 5 | PX SEND HASH | :TQ10000 | Q1,00 | P->P | HASH | | 6 | PX BLOCK ITERATOR | | Q1,00 | PCWC | | | 7 | TABLE ACCESS FULL| SALES | Q1,00 | PCWP | | ------------------------------------------------------------------------- ① P→Pに注⽬します。P→P(Prallel to Parallel)はPXセットが次のPXセットにデータを 送るという意味で、各PXセットの処理の境⽬と なります SELECT prod_id,SUM(amount_sold) FROM sales GROUP BY prod_id;
  9. IN-OUT列 11 Copyright © 2026, Oracle and/or its affiliates プロセス間通信

    プロセス間通信する⽅式 P->P: Parallel to Parallel • PXが次のPXサーバー・セットに データを送る P->S: Parallel to Serial • PXがQCにデータを送る S->P: Serial to Parallel • QCがPXにデータを送る • このステップはシリアル実⾏ プロセス間通信しない⽅式 PCWP: Parallel Combined with Parent • 次のステップも同⼀のPXが⾏なう PCWC: Parallel Combined with Child • 前のステップと同⼀のPXが⾏なう こちらの2つが出ている間はPXセット間で通信 が発⽣していない。 つまり同じPXセットで処理しつづけていることに なります
  10. パラレル実⾏の動作イメージを実⾏計画からつかむ 12 Copyright © 2026, Oracle and/or its affiliates -------------------------------------------------------------------------

    | Id | Operation | Name | TQ |IN-OUT| PQ Distrib | ------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | | | 1 | PX COORDINATOR | | | | | | 2 | PX SEND QC (RANDOM) | :TQ10001 | Q1,01 | P->S | QC (RAND) | | 3 | HASH GROUP BY | | Q1,01 | PCWP | | | 4 | PX RECEIVE | | Q1,01 | PCWP | | | 5 | PX SEND HASH | :TQ10000 | Q1,00 | P->P | HASH | | 6 | PX BLOCK ITERATOR | | Q1,00 | PCWC | | | 7 | TABLE ACCESS FULL| SALES | Q1,00 | PCWP | | ------------------------------------------------------------------------- SALES PX PX TQ10000 PX PX TQ10001 QC PXセット00 PXセット01 ②後ろの2桁の数字がPXセットの番号に該当、 TQの後ろ2桁も同様。Q1はQCの番号 ②TQの後ろ2桁の数字、もしくはTQ名の後ろ2 桁がPXセットの番号です。Q1はQCの番号。① の線を境に番号が変わっていることを確認。PX セットの番号ごとに背景⾊を変えます TQはPXセットの実⾏結果を次のPXセットに渡す ための置き場のイメージ(※) SELECT prod_id,SUM(amount_sold) FROM sales GROUP BY prod_id; (※)TQの補⾜ TQ︓テーブル・キューは抽象概念で実際に メモリ上にテーブルのような形で⽤意されるわ けではありません。 実際には各プロセスがそれぞれキューを持っ ており、そこに置かれたデータをキュー・リファレ ンスという番号を使ってリンクしていています。 読み取ったデータは各プロセスのPGAから PGAに直接コピーされます
  11. パラレル実⾏の動作イメージを実⾏計画からつかむ 13 Copyright © 2026, Oracle and/or its affiliates -------------------------------------------------------------------------

    | Id | Operation | Name | TQ |IN-OUT| PQ Distrib | ------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | | | 1 | PX COORDINATOR | | | | | | 2 | PX SEND QC (RANDOM) | :TQ10001 | Q1,01 | P->S | QC (RAND) | | 3 | HASH GROUP BY | | Q1,01 | PCWP | | | 4 | PX RECEIVE | | Q1,01 | PCWP | | | 5 | PX SEND HASH | :TQ10000 | Q1,00 | P->P | HASH | | 6 | PX BLOCK ITERATOR | | Q1,00 | PCWC | | | 7 | TABLE ACCESS FULL| SALES | Q1,00 | PCWP | | ------------------------------------------------------------------------- SALES PX PX スキャン担当 TQ10000 PX PX GROUP BY担当 TQ10001 QC PXセット00 PXセット01 ③ それぞれのPXセッ ト内の実⾏計画からど んな処理をパラレル実 ⾏しているかつかむ ③ それぞれのPXセッ ト内の実⾏計画から どんな処理をパラレル 実⾏しているかつかむ SELECT prod_id,SUM(amount_sold) FROM sales GROUP BY prod_id; Tips︓ リアルタイムSQL監視では、PXセットご とに先頭の⼈マーク部分で⾊分けされ ています
  12. パラレル実⾏の動作イメージを実⾏計画からつかむ 14 Copyright © 2026, Oracle and/or its affiliates -------------------------------------------------------------------------

    | Id | Operation | Name | TQ |IN-OUT| PQ Distrib | ------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | | | 1 | PX COORDINATOR | | | | | | 2 | PX SEND QC (RANDOM) | :TQ10001 | Q1,01 | P->S | QC (RAND) | | 3 | HASH GROUP BY | | Q1,01 | PCWP | | | 4 | PX RECEIVE | | Q1,01 | PCWP | | | 5 | PX SEND HASH | :TQ10000 | Q1,00 | P->P | HASH | | 6 | PX BLOCK ITERATOR | | Q1,00 | PCWC | | | 7 | TABLE ACCESS FULL| SALES | Q1,00 | PCWP | | ------------------------------------------------------------------------- SALES PX PX スキャン担当 TQ10000 PX PX GROUP BY担当 TQ10001 QC PXセット00 PXセット01 PX BLOCK ITERATOR PX SEND HASH PX SEND QC (RANDOM) QC(RAND) 処理が終わった順にTQにおく ④PX XXX関連のオ ペレーションの役割に ついて把握する SELECT prod_id,SUM(amount_sold) FROM sales GROUP BY prod_id; PX RECEIVE
  13. スキャン時のPXへのデータ割り当て⽅式 15 Copyright © 2026, Oracle and/or its affiliates ブロック・レンジ・グラニュルとパーティション・グラニュル

    ブロック・レンジ・グラニュル • PX BLOCK ITERATOR • ブロック単位で分割してアクセス • パーティション表に対してもブロック・レンジ・グラニュル は使⽤されることはあります パーティション・グラニュル • PX PARTITION RANGE ALL • PX PARTITION HASH ALL • パーティション単位の論理分割を利⽤したい場合 • パーティション単位に分割してアクセス - パーティション・ワイズ結合など - インデックス・スキャンのパラレル実⾏でも利⽤ • インデックス・ファースト・フル・スキャンでなくとも パーティションであればパラレルインデックススキャンが可能 PX スキャン PX PX PX QC P1 表 P2 P3 P4 PX スキャン PX PX PX QC 表
  14. PX SEND xxxまたはPQ Distrib 列 16 Copyright © 2026, Oracle

    and/or its affiliates P->Pでの分散⽅式のみ抜粋 HASH(基本はこれを使用する) • 大量のデータを分割するのに最適。ハッシュ分散し、同じキーを同じPXへ送る • 結合やGroup Byでの基本的な方式 • 重複データが多いときに、データの偏り(データ・スキュー)が発生 BROADCAST(結合の片方が小さい) • 結合で片方が小さい場合利用される方式 • 小さい方のデータセットをすべてのPXに配布 RANGE(ソートなど) • ソート・キーで分散、PXへの配分をキー順を保ったレンジで分ける ROUND-ROBIN • 行を順番にPXに配る方式 • PXへのデータの均等配布目的で使用 PARTITION • パーティション分割(片方がパーティション表のときのパーシャル・パーティション・ワイズ結合)
  15. データ・スキュー 18 Copyright © 2026, Oracle and/or its affiliates データ・スキューとPX間処理量のかたよりが起こす性能問題

    PX1 PX2 PX3 PX4 理想的なパラレル実⾏ 各PXに対して処理するデータ量が同じ。 各PXの処理が同時に終わり、PXの待ちが発⽣しない PX1 PX2 PX3 PX4 データ・スキューが発⽣している場合 各PXで処理するデータ量が異なる 偏りが⼤きいPXの終了を、他のPXが待つ形になる PX SEND HASHでは、結合キーや集約キーのハッシュ値で各 ⾏の送り先PXが決まります。キー値に偏りがあると、同じキーの ⾏は同じPXに集まるため、⼀部のPXだけが⼤量の⾏を処理し、 そこがボトルネックになります。
  16. 結合(HASH JOIN)でのPX SEND HASHのスキュー発⽣ 19 Copyright © 2026, Oracle and/or

    its affiliates -------------------------------------------------------------------------- | Id | Operation | Name | TQ |IN-OUT| PQ Distrib | -------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | | | 1 | PX COORDINATOR | | | | | | 2 | PX SEND QC (RANDOM) | :TQ10002 | Q1,02 | P->S | QC (RAND) | |* 3 | HASH JOIN | | Q1,02 | PCWP | | | 4 | PX RECEIVE | | Q1,02 | PCWP | | | 5 | PX SEND HASH | :TQ10000 | Q1,00 | P->P | HASH | | 6 | PX BLOCK ITERATOR | | Q1,00 | PCWC | | | 7 | TABLE ACCESS FULL| CUSTOMERS | Q1,00 | PCWP | | | 8 | PX RECEIVE | | Q1,02 | PCWP | | | 9 | PX SEND HASH | :TQ10001 | Q1,01 | P->P | HASH | | 10 | PX BLOCK ITERATOR | | Q1,01 | PCWC | | |* 11 | TABLE ACCESS FULL| SALES | Q1,01 | PCWP | | -------------------------------------------------------------------------- SELECT c.cust_email FROM sales s, customers c WHERE s.cust_id = c.cust_id AND s.amount_sold >= 1000 以下のSQLの実⾏計画を例に具体的なケースをみていきます データ・スキューの問題をみるため、特定顧客の注⽂が多いようなケースを想定します SALES表には特定顧客ID(例えばcustomer_id=1)の注⽂データが多数を占めている状況をイメージをしてく ださい
  17. 結合(HASH JOIN)でのPX SEND HASHのスキュー発⽣ 20 Copyright © 2026, Oracle and/or

    its affiliates SALES PX PX スキャン担当 TQ10001 PXセット01 PX BLOCK ITERATOR PX SEND HASH CUSTOMERS PX PX スキャン担当 TQ10000 PXセット00 PX BLOCK ITERATOR PX SEND HASH PX PX QC PXセット02 TQ10002 ハッシュジョイン担当 ビルド プローブ スキューにより上のPXのハッ シュ・ジョインの⽅が重い スキュー発⽣ PX RECEIVE PX RECEIVE
  18. V$PQ_TQSTATによるスキューの観測⽅法 21 Copyright © 2026, Oracle and/or its affiliates 各PXセットのProducerとComsumerのPX毎の⾏数がわかるためスキューの具合がわかります

    select tq_id,server_type,num_rows,process from v$pq_tqstat order by tq_id,server_type desc,process; TQ_ID SERVER_TYP NUM_ROWS PROCES ---------- ---------- ---------- ------ 0 Producer 0 P002 0 Producer 5 P003 0 Consumer 2 P000 0 Consumer 3 P001 1 Producer 54353 P002 1 Producer 45647 P003 1 Consumer 2 P000 1 Consumer 99998 P001 2 Producer 1 P000 2 Producer 1 P001 2 Consumer 2 QC 外部表 CUSTOMERS側 内部表 SALES側 PX BLOCK ITERATOR PX RECEIVE= PX SEND HASH PX RECEIVE= PX SEND HASH PX BLOCK ITERATOR Tips︓ リアルタイムSQL監視では、各PXセット内でPX毎の DB Time/IO Count/Buffer Getsを確認するこ とで、スキューの発⽣を観測することができます スキュー発⽣
  19. 解決策︓BROADCASTによるデータ・スキュー解決 22 Copyright © 2026, Oracle and/or its affiliates ⼤きな表と⼩さな表の結合のときにオプティマイザが判断して実⾏

    ⼩さい表を、⼤きい表側をスキャンした各PXに配布(BROADCAST)し、その場で結合 • スキューを回避した形でハッシュジョインの並列化が可能 • ⼤きい表側のSEND HASHを防ぐことで、スキューの発⽣を防ぐだけでなく、PX間データ送信も防ぐ -------------------------------------------------------------------------- | Id | Operation | Name | TQ |IN-OUT| PQ Distrib | -------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | | | 1 | PX COORDINATOR | | | | | | 2 | PX SEND QC (RANDOM) | :TQ10001 | Q1,01 | P->S | QC (RAND) | |* 3 | HASH JOIN | | Q1,01 | PCWP | | | 4 | PX RECEIVE | | Q1,01 | PCWP | | | 5 | PX SEND BROADCAST | :TQ10000 | Q1,00 | P->P | BROADCAST | | 6 | PX BLOCK ITERATOR | | Q1,00 | PCWC | | |* 7 | TABLE ACCESS FULL| COUNTRIES | Q1,00 | PCWP | | | 8 | PX BLOCK ITERATOR | | Q1,01 | PCWC | | | 9 | TABLE ACCESS FULL | CUSTOMERS | Q1,01 | PCWP | | -------------------------------------------------------------------------- SELECT cu.cust_email FROM customers cu, countries co WHERE cu.country_id = co.country_id AND co.country_region = 'Asia'
  20. 解決策︓BROADCASTによる解決 23 Copyright © 2026, Oracle and/or its affiliates 外部表をスキャンした

    PXセットがハッシュジョイ ンも実⾏することで SEND HASHを回避 Tips︓ 統計情報が不正確で、BROADCASTした データセットが、実は⼤きかった場合、PX間 通信がボトルネックになります。 このトピックは後ででてきます CUSTOMERS PX PX スキャン&ハッシュジョイン担当 PXセット01 PX BLOCK ITERATOR COUNTRIES PX PX スキャン担当 TQ10000 PXセット00 PX BLOCK ITERATOR QC TQ10001 PX SEND BROADCAST PX SEND QC(RANDOM) PX RECEIVE
  21. 解決策︓Small Table Replicate(12c以降) 24 Copyright © 2026, Oracle and/or its

    affiliates ⼩さい表はPXセット作ってBROADCASTしなくとも、その場で読めばいいという発想 CUSTOMERS PX PX スキャン&ハッシュジョイン担当 PXセット00 PX BLOCK ITERATOR COUNTRIES QC TQ10001 COUNTRIES PX SEND QC(RANDOM) PXセット00が⼩さい表(COUNTRIES)を読 み取ることで、PX BROADCASTも回避
  22. データ・スキューへのSQLでの対策︓ホット・キー分離 25 Copyright © 2026, Oracle and/or its affiliates ホットキー側はBROADCAST、⾮ホット・キー側はPX

    SEND HASH ホット・キーとはデータが偏って多くの⾏をしめているキーのこと。 SELECT c.customer_segment, SUM(s.amount) AS amount_sum FROM sales_fact s JOIN dim_customer c ON c.customer_id = s.customer_id WHERE s.sales_date >= DATE '2026-05-01' AND s.sales_date < DATE '2026-06-01' GROUP BY c.customer_segment; サンプルSQL(ホット・キー分離前) • ホット・キーはcustomer_id=1,2(これは割り出せていることが前提) • 主要取引先がほぼ決まっているような想定 SQL書き換え⽅針︓ • 元のSQLを、ホット・キー側と⾮ホット・キー側にわけてUNIONする • ホットキー側はBROADCAST、⾮ホット・キー側はPX SEND HASHを期待
  23. データ・スキューへのSQLでの対策の例 26 Copyright © 2026, Oracle and/or its affiliates ホット・キー分離

    /* customer_id=1,2がホット・キー */ WITH hot_keys AS ( SELECT 1 AS customer_id FROM dual UNION ALL SELECT 2 AS customer_id FROM dual ) SELECT customer_segment, SUM(amount_sum) AS amount_sum FROM ( /* ホットキー⽤ブランチ */ SELECT c.customer_segment, SUM(s.amount) AS amount_sum FROM sales_fact s JOIN dim_customer c ON c.customer_id = s.customer_id WHERE s.sales_date >= DATE '2026-05-01' AND s.sales_date < DATE '2026-06-01' AND EXISTS ( SELECT 1 FROM hot_keys hk WHERE hk.customer_id = s.customer_id ) GROUP BY c.customer_segment UNION ALL /* ⾮ホットキー⽤ブランチ*/ SELECT c.customer_segment, SUM(s.amount) AS amount_sum FROM sales_fact s JOIN dim_customer c ON c.customer_id = s.customer_id WHERE s.sales_date >= DATE '2026-05-01' AND s.sales_date < DATE '2026-06-01' AND NOT EXISTS ( SELECT 1 FROM hot_keys hk WHERE hk.customer_id = s.customer_id ) GROUP BY c.customer_segment ) GROUP BY customer_segment; ↗につづく
  24. データ・スキューへのSQLでの対策の例 27 Copyright © 2026, Oracle and/or its affiliates ホット・キー分離

    /* customer_id=1,2がホット・キー */ WITH hot_keys AS ( SELECT 1 AS customer_id FROM dual UNION ALL SELECT 2 AS customer_id FROM dual ) SELECT customer_segment, SUM(amount_sum) AS amount_sum FROM ( /* ホットキー⽤ブランチ */ SELECT c.customer_segment, SUM(s.amount) AS amount_sum FROM sales_fact s JOIN dim_customer c ON c.customer_id = s.customer_id WHERE s.sales_date >= DATE '2026-05-01' AND s.sales_date < DATE '2026-06-01' AND EXISTS ( SELECT 1 FROM hot_keys hk WHERE hk.customer_id = s.customer_id ) GROUP BY c.customer_segment UNION ALL /* ⾮ホットキー⽤ブランチ*/ SELECT c.customer_segment, SUM(s.amount) AS amount_sum FROM sales_fact s JOIN dim_customer c ON c.customer_id = s.customer_id WHERE s.sales_date >= DATE '2026-05-01' AND s.sales_date < DATE '2026-06-01' AND NOT EXISTS ( SELECT 1 FROM hot_keys hk WHERE hk.customer_id = s.customer_id ) GROUP BY c.customer_segment ) GROUP BY customer_segment; ホット・キー側はBROADCAST HASH JOIN PX RECEIVE PX SEND BROADCAST -- DIM_CUSTOMER側 TABLE ACCESS FULL DIM_CUSTOMER PX BLOCK ITERATOR -- SALES_FACT側 TABLE ACCESS FULL SALES_FACT ⾮ホット・キー側はPX SEND HASH HASH JOIN PX RECEIVE -- DIM_CUSTOMER側 PX SEND HASH PX RECEIVE -- SALES_FACT側 PX SEND HASH Tips︓ 実は、SQLはそのままで、これと似た動作をする機能が12cからあ ります。PX SEND HYBRID HASH(SKEW)です(後述) ↗につづく
  25. パラレル間データ通信 29 Copyright © 2026, Oracle and/or its affiliates PXセット間の通信で問題が発⽣するケース

    PXセット間の通信で問題が発⽣するケースを 扱います 問題が発⽣する代表例︓ SEND HASHすべき⼤きめの表を BROADCASTしてしまった また、PXセット間の通信を抑えるために使える オラクルの機能も紹介します • GROUP BY PUSH DOWN • パーティション・ワイズ結合 ここ ここ
  26. 適応型パラレル分散⽅法︓PX SEND HYBRID HASH(12c以降) 30 Copyright © 2026, Oracle and/or

    its affiliates ⾒積もりミスによる⼤きいデータのBROADCASTを防ぐ 適応型パラレル分散方法: オプティマイザが統計情報だけでは、正確に⾒積もれないと判断した場合、実⾏計画作成時点では 分散⽅法を決定せず、実⾏時の⾏数を考慮して、分散⽅法を決定する機能 ハッシュジョインの外部表側の ⾏数が多ければ HASH ⾏数が少なければ BROADCAST 判断の閾値は、外部表側の実際の⾏数がパラレル度の2倍 これによって、実際に⾏数が多い場合に、誤ってBROADCASTされてしまうことを抑制できます 適応計画(Adaptive Plans)の機能のひとつ。デフォルト有効(19c,26ai)
  27. 適応型パラレル分散⽅法︓PX SEND HYBRID HASH 31 Copyright © 2026, Oracle and/or

    its affiliates 実⾏計画の分散⽅法の確認 SQL> SELECT COUNT(*) FROM ( SELECT /*+ pq_distribute(t1,hash,hash) */ * FROM t3,t1 WHERE t3.col1 = t1.col1); ----------------------------------------------------------------------------------------------- ---------- | Id | Operation | Name | E-Rows | TQ |IN-OUT| PQ Distrib | A-Rows | | A-Rows | ----------------------------------------------------------------------------------------------- ---------- | 0 | SELECT STATEMENT | | | | | | 1 | | 1 | | 1 | SORT AGGREGATE | | 1 | | | | 1 | | 1 | | 2 | PX COORDINATOR | | | | | | 2 | | 4 | | 3 | PX SEND QC (RANDOM) | :TQ10002 | 1 | Q1,02 | P->S | QC (RAND) | 0 | | 0 | | 4 | SORT AGGREGATE | | 1 | Q1,02 | PCWP | | 2 | | 4 | |* 5 | HASH JOIN | | 50000 | Q1,02 | PCWP | | 100K| | 100K| | 6 | PX RECEIVE | | 5 | Q1,02 | PCWP | | 5 | | 20 | | 7 | PX SEND HYBRID HASH | :TQ10000 | 5 | Q1,00 | P->P | HYBRID HASH| 0 | | 0 | | 8 | STATISTICS COLLECTOR | | | Q1,00 | PCWC | | 5 | | 5 | | 9 | PX BLOCK ITERATOR | | 5 | Q1,00 | PCWC | | 5 | | 5 | |* 10 | TABLE ACCESS FULL | T3 | 5 | Q1,00 | PCWP | | 5 | | 5 | | 11 | PX RECEIVE | | 100K| Q1,02 | PCWP | | 100K| | 100K| | 12 | PX SEND HYBRID HASH | :TQ10001 | 100K| Q1,01 | P->P | HYBRID HASH| 0 | | 0 | | 13 | PX BLOCK ITERATOR | | 100K| Q1,01 | PCWC | | 100K| | 100K| |* 14 | TABLE ACCESS FULL | T1 | 100K| Q1,01 | PCWP | | 100K| | 100K| 津島博士のパフォーマンス講座 第39回 https://blogs.oracle.com/otnjp/tsushima-hakushi-39 パラレル2 閾値 4 パラレル4 閾値 8 外部表側の件数は5件 外部表側の件数が 閾値より上︓HASH 閾値より下︓BROADCAST (閾値はパラレル度の2倍) HASH BROADCAST 5件 ×4 パラレル = 20 件 Tips︓ A-RowsのBROADCASTの件数に注意 各PXにBROADCASTして膨らんだ件数が 出⼒される Tips︓ リアルタイムSQL監視では、Other列の双眼 鏡アイコンからどの分散⽅法が選択されたか 確認できます。 Distribution method: 5︓ROUND-ROBIN 6︓BROADCAST 16︓HASH
  28. 適応型パラレル分散⽅法︓PX SEND HYBRID HASH 32 Copyright © 2026, Oracle and/or

    its affiliates 外部表側がBROADCASTになった場合は、内部表側はROUND-ROBIN 津島博士のパフォーマンス講座 第39回 https://blogs.oracle.com/otnjp/tsushima-hakushi-39 HYBRID HASHで外部表側がBROADCASTになった場合、内部表側 ではROUND-ROBINによって均等分散配布を⾏います。 通常のBROADCASTでは、内部表側をスキャンしたPXセットがそのまま ハッシュ・ジョインし、PX SEND処理を回避していましたが、 HYBRID HASHではそうはなりません。 これはHYBRID HASHでは、実⾏計画を⽴てた時点で、内部表のスキャ ンとハッシュジョインのPXセットは別と指定された上で、後から均等配布する ための制限と考えられます T1 PX PX スキャン担当 TQ10001 PXセット01 PX BLOCK ITERATOR T3 PX PX スキャン担当 TQ10000 PXセット00 PX BLOCK ITERATOR PX PX QC TQ10002 ハッシュジョイン担当 ビルド プローブ PX SEND HYBRID HASH (ROUND- ROBIN) PX SEND HYBRID HASH (BROADCAST) 外部表側 内部表側 参考︓通常のBROADCASTのハッシュ・ジョイン PX RECEIVE PX RECEIVE PXセット02
  29. PQ SEND HYBRID HASH(SKEW) 33 Copyright © 2026, Oracle and/or

    its affiliates SQL変更なしで重複値の多いホット・キーにはBROADCAST、⾮ホット・キーはSEND HASHを実施 津島博士のパフォーマンス講座 第39回 https://blogs.oracle.com/otnjp/tsushima-hakushi-39 SQLのホット・キー分割をせずに、ホット・キー分割を実現 • ホット・キー側はBROADCAST • ⾮ホット・キー側はHASH 結合列にヒストグラムが存在するかPQ_SKEWヒントを使⽤していた場合に出現
  30. PQ SEND HYBRID HASH(SKEW) 34 Copyright © 2026, Oracle and/or

    its affiliates 実⾏計画 津島博士のパフォーマンス講座 第39回 https://blogs.oracle.com/otnjp/tsushima-hakushi-39 SQL> ALTER SESSION FORCE PARALLEL QUERY PARALLEL 2; SQL> SELECT COUNT(*) FROM (SELECT /*+ pq_distribute(t1,hash,hash) pq_skew(t1) */ * FROM t3,t1 WHERE t3.col1 = t1.col1); --------------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Starts | E-Rows | | TQ |IN-OUT| PQ Distrib | A-Rows | --------------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | | | | | 1 | | 1 | SORT AGGREGATE | | 1 | 1 | | | | | 1 | | 2 | PX COORDINATOR | | 1 | | | | | | 2 | | 3 | PX SEND QC (RANDOM) | :TQ10002 | 0 | 1 | | Q1,02 | P->S | QC (RAND) | 0 | | 4 | SORT AGGREGATE | | 2 | 1 | | Q1,02 | PCWP | | 2 | |* 5 | HASH JOIN | | 2 | 50000 | | Q1,02 | PCWP | | 100K| | 6 | PX RECEIVE | | 2 | 5 | | Q1,02 | PCWP | | 6 | | 7 | PX SEND HYBRID HASH | :TQ10000 | 0 | 5 | | Q1,00 | P->P | HYBRID HASH| 0 | | 8 | STATISTICS COLLECTOR | | 2 | | | Q1,00 | PCWC | | 5 | | 9 | PX BLOCK ITERATOR | | 2 | 5 | | Q1,00 | PCWC | | 5 | |* 10 | TABLE ACCESS FULL | T3 | 1 | 5 | | Q1,00 | PCWP | | 5 | | 11 | PX RECEIVE | | 2 | 100K| | Q1,02 | PCWP | | 100K| | 12 | PX SEND HYBRID HASH (SKEW)| :TQ10001 | 0 | 100K| | Q1,01 | P->P | HYBRID HASH| 0 | | 13 | PX BLOCK ITERATOR | | 2 | 100K| | Q1,01 | PCWC | | 100K| |* 14 | TABLE ACCESS FULL | T1 | 26 | 100K| | Q1,01 | PCWP | | 100K| T3︓全5⾏ (内部表側) COL1 COUNT(*) ---------- ---------- 1 1 2 1 3 1 4 1 100 1 T1︓全100,000⾏ (外部表側) COL1 COUNT(*) ---------- ---------- 1 1 2 1 3 1 4 1 100 99996 ホット・キー → ホット・キー → ホット・キーの100は2パラレルに BROADCASTするので2 ⾮ホット・キーは残り4つと 合わせて6 T1のSCANと別のPXセットで結合 するため、内部表側を均等配布す るのための、ROUND-ROBINは必 要
  31. PQ SEND HYBRID HASH(SKEW) 35 Copyright © 2026, Oracle and/or

    its affiliates v$pq_tqstatから確認 SQL> select tq_id,server_type,num_rows,process from v$pq_tqstat order by tq_id,server_type desc,process; TQ_ID SERVER_TYPE NUM_ROWS PROCESS ---------- -------------------- ---------- ---------- 0 Producer 6 P002 <-- 100を2個にして6行に 0 Producer 0 P003 0 Consumer 3 P000 <-- どちらにも100をいれる 0 Consumer 3 P001 <-- どちらにも100をいれる 1 Producer 49903 P002 1 Producer 50097 P003 1 Consumer 50001 P000 1 Consumer 49999 P001 2 Producer 1 P000 2 Producer 1 P001 2 Consumer 2 QC 津島博士のパフォーマンス講座 第39回 https://blogs.oracle.com/otnjp/tsushima-hakushi-39 外部表T3側 内部表T1側 スキューの回避を確認 PX BLOCK ITERATOR PX RECEIVE= PX SEND HYBRID HASH PX RECEIVE= PX SEND HYBRID HASH (SKEW) PX BLOCK ITERATOR
  32. GROUP BY PUSH DOWN(GPD) 36 Copyright © 2026, Oracle and/or

    its affiliates GROUP BYを早い段階で部分集計し、PXセット間に流すデータ量を削減する仕組み • 部分集計と最終集計の2段階でGROUP BYが発⽣するコストと、PXセット間のデータ量が削減されるコストを オプテイマイザが⽐較して⾃動決定 • ユニーク度が⾼くGROUP BYの結果、結果件数があまり減らない集計処理には、GPDは不向き これもオプティマイザが判断 • RACのインターノード・パラレル実⾏の場合、通信量削減はさらに効果的 • 1PXあたりに求められる最⼤PGA量を低くする効果もあり • DISTINCTではGPDが発⽣しないので、GROUP BYに書き換えることで⾼速化することがある
  33. GROUP BY PUSH DOWN 37 Copyright © 2026, Oracle and/or

    its affiliates GROUP BY PUSH DOWNの有無の⽐較 SELECT prod_id,SUM(amount_sold) FROM sales GROUP BY prod_id ------------------------------------------------------------------------- | Id | Operation | Name | TQ |IN-OUT| PQ Distrib | ------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | | | 1 | PX COORDINATOR | | | | | | 2 | PX SEND QC (RANDOM) | :TQ10001 | Q1,01 | P->S | QC (RAND) | | 3 | HASH GROUP BY | | Q1,01 | PCWP | | | 4 | PX RECEIVE | | Q1,01 | PCWP | | | 5 | PX SEND HASH | :TQ10000 | Q1,00 | P->P | HASH | | 6 | PX BLOCK ITERATOR | | Q1,00 | PCWC | | | 7 | TABLE ACCESS FULL| SALES | Q1,00 | PCWP | | ------------------------------------------------------------------------- GROUP BY PUSH DOWNなしの場合 GROUP BY PUSH DOWNありの場合 SELECT prod_id,SUM(amount_sold) FROM sales GROUP BY prod_id -------------------------------------------------------------------------- | Id | Operation | Name | TQ |IN-OUT| PQ Distrib | -------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | | | 1 | PX COORDINATOR | | | | | | 2 | PX SEND QC (RANDOM) | :TQ10001 | Q1,01 | P->S | QC (RAND) | | 3 | HASH GROUP BY | | Q1,01 | PCWP | | | 4 | PX RECEIVE | | Q1,01 | PCWP | | | 5 | PX SEND HASH | :TQ10000 | Q1,00 | P->P | HASH | | 6 | HASH GROUP BY | | Q1,00 | PCWP | | | 7 | PX BLOCK ITERATOR | | Q1,00 | PCWC | | | 8 | TABLE ACCESS FULL| SALES | Q1,00 | PCWP | | -------------------------------------------------------------------------- PX SEND HASHの前に部分集計を実施して、 PX間通信に必要なデータ量を削減 PX RECEIVE PX RECEIVE
  34. パーティション・ワイズ結合 38 Copyright © 2026, Oracle and/or its affiliates 最初からパラレル結合可能な形で分割されているため、再分配するPX間送信が必要ない

    2個の表が両⽅とも結合キーでパーティション化されている場合 SELECT s.prod_id, c.unit_price FROM sales s, costs c WHERE s.prod_id = c.prod_id AND s.time_id = c.time_id AND s.promo_id = c.promo_id AND s.channel_id = c.channel_id AND s.amount_sold >= 1000 AND c.unit_price >= 1000 -------------------------------------------------------------------------- | Id | Operation | Name | TQ |IN-OUT| PQ Distrib | -------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | | | 1 | PX COORDINATOR | | | | | | 2 | PX SEND QC (RANDOM) | :TQ10000 | Q1,00 | P->S | QC (RAND) | | 3 | PX PARTITION RANGE ALL| | Q1,00 | PCWC | | |* 4 | HASH JOIN | | Q1,00 | PCWP | | |* 5 | TABLE ACCESS FULL | COSTS | Q1,00 | PCWP | | |* 6 | TABLE ACCESS FULL | SALES | Q1,00 | PCWP | | --------------------------------------------------------------------------- PXセットがひとつだけ、つまり PXセット間の通信が発⽣していない
  35. まとめ 40 Copyright © 2026, Oracle and/or its affiliates 1.

    SQLのパラレル実⾏のイメージをつかむ ・イントラ・オペレーション並列化とインターオペレーション並列化 ・パラレル実⾏の動作イメージを実⾏計画からつかむ 2. パラレル実⾏の課題1︓データ・スキュー ・ 結合(HASH JOIN)でのPX SEND HASHのスキュー発⽣ ・ BROADCASTおよびSmall Table Replicateによる解決 ・ SQLのホット・キー分離書換えによる解決 3. パラレル実⾏の課題2︓パラレル間データ通信 ・ 誤って⼤きい表をBROADCASTする問題 ・ PQ SEND HYBRID HASHによる解決 PQ SEND HYBRID HASH(SKEW) ・ GROUP BY PUSH DOWN ・ パーティション・ワイズ結合
  36. 参考情報 41 Copyright © 2026, Oracle and/or its affiliates 特に参考にしたもの★をつけています

    マニュアル︓VLDBおよびパーティショニング・ガイド • https://docs.oracle.com/cd/E82638_01/vldbg/using-parallel.html ホワイトペーパー︓Oracle Databaseでのパラレル実行 • https://www.oracle.com/technetwork/jp/database/bi-datawarehousing/twp-parallel-execution-fundamentals- 133639-ja.pdf ★津島博⼠のパフォーマンス講座 第20回,第39回 • https://blogs.oracle.com/otnjp/tsushima-hakushi-20 • https://blogs.oracle.com/otnjp/tsushima-hakushi-39 Oracle Database Technology Night #48 津島博⼠のパフォーマンス講座 - SQLチューニングは実⾏計画から Oracle Database Technology Night #48 津島博⼠のパフォーマンス講座 - SQLチューニングは実⾏計画から (Q&A) • https://speakerdeck.com/oracle4engineer/oracle-database-technology-night-number-48-jin-dao-bo-shi- falsepahuomansujiang-zuo-sqltiyuninguhashi-xing-ji-hua-kara • https://speakerdeck.com/oracle4engineer/oracle-database-technology-night-number-48-jin-dao-bo-shi- falsepahuomansujiang-zuo-sqltiyuninguhashi-xing-ji-hua-kara-q-and-a ★シバタツ流︕ パラレル・クエリーの徹底活⽤とチューニングの極意 • https://www.oracle.com/webfolder/technetwork/jp/ondemand/ddd2013/A-4.pdf