株式会社コーソル

コーソルDatabaseエンジニアのブログ

技術ブログ

#dbts2024 「SQL実行計画 – 主要RDBMS製品の比較(Oracle, MySQL, PostgreSQL)」内容紹介+資料/動画公開

Oracle ACE Proの渡部です。

2024年7月11日(木)~12日(金)で開催されたdb tech showcase 2024 Tokyoで、担当したセッション「SQL実行計画 - 主要RDBMS製品の比較(Oracle, MySQL, PostgreSQL)」の内容をご紹介しつつ、併せて、セッション資料のダウンロード方法、アーカイブ動画の視聴方法もご紹介させていただきます。

db tech showcase 2024 Tokyoとは

db tech showcaseは、株式会社インサイトテクノロジー様が主催する、国内最大級のデータ技術カンファレンスです。2012年から開催されています。

2024年のテーマは「テックギークに告ぐ。自らをファインチューニングせよ! - データ技術を鍛える100本ノック。これがdbtsの筋トレだ!- 」です!

なお、開催形式はフルオフライン形式のみでオンライン配信(ライブ)はありませんでしたが、後ほどご説明するアーカイブ配信サイトからアーカイブ動画を視聴できます

  • db tech showcase 2024 Tokyo
  • 開催期間 2024年7月11日(木)-12日(金)
  • 場所 TKP市ヶ谷カンファレンスセンター
  • 参加費 : 無料(要 登録)

セミナー「SQL実行計画 - 主要RDBMS製品の比較(Oracle, MySQL, PostgreSQL)」の概要

db tech showcase 2024 Tokyoで、弊社からは以下のセッションをお送りしました。

  • タイトル : SQL実行計画 - 主要RDBMS製品の比較(Oracle, MySQL, PostgreSQL)
  • 日時 : 2024年7月11日 (木) 11:45 - 12:15(セッション C3)
  • 講演者 : 渡部 亮太 (Oracle ACE Pro)

Oracle, MySQL, PostgreSQLを対象に、実行計画に関連することがらを比較することで、データベースに関する理解を深めることを狙ったセッションです。 セッション全体としてのストーリーは特になく、気になった点をピックアップしてお話しする形で進めます。 いずれかのRDBMS製品の実行計画についてある程度の知識があることを前提とします。上級者向けのセッションと位置づけ、懇切丁寧には説明しません。

実行計画とは

  • SQLを実行する"内部的に使用される"手順
  • 実行計画は、RDBMS内部のコンポーネントであるオプティマイザ(プランナ)が作成する
  • オプティマイザは、オプティマイザ統計などの情報を元に、どういう手順が最適か?を予測し、最適と予測される手順を実行計画に採用する
  • 実行計画は、"一般に" ツリー構造で示される

  • 実行計画の"本体"は「ツリー構造をしたステップ」だが、これ以外にも「各ステップの統計値」、「補足情報」が表示される。

  • 実行計画の各段階、各フェーズに対応する概念が「ステップ」
    • 実行計画の実行手順における実行要素
    • 親子関係がある
  • 各ステップで実行される処理(操作)の内容を示すのが「オペレーション」
    • 各ステップで実行される操作の種類
    • TABLE ACCESS FULL、 INDEX RANGE SCAN など

なお、使用される用語はRDBMS製品によって異なる。上記の語用は、Oracle Databaseのもの。

実行計画の取得

  • EXPLANコマンドを使用して実行計画を表示するが、詳細はRDBMS製品によって異なる

  • 実行計画および統計には、見積(予測)と 実行時(実測)の2種類がある点に注意すること

    • 特に、見積統計を実行時統計と勘違いすると、チューニング施策がトンチンカンになる!
    • チューニングは、一般に、実行時統計(実測)を改善するように行うべき
  • → 状況が許すなら、できるかぎり実行時実行計画を確認するべき

    • 注意: SQLが実際に実行されるため、実行時実行計画を確認できない/すべきでない状況も十分ありうる

実行計画のオペレーション

  • オペレーションに関するマニュアルの記載は、一般に十分ではない

    • Oracleは比較的充実しているが
  • 「オペレーション」はOracle用語。PostgreSQLでは「クエリオペレータ」

  • 念のための注意: 考え方が異なるので、オペレーションの種類の個数を単純に比較することはできない

主キーアクセス - 索引アクセス

SELECT * FROM t1 WHERE pk=1;

Oracle

----------------------------------------------------
| Id  | Operation                   | Name        | 
----------------------------------------------------
|   0 | SELECT STATEMENT            |             | 
|   1 |  TABLE ACCESS BY INDEX ROWID| T1          | 
|*  2 |   INDEX UNIQUE SCAN         | PK_T1       | 
----------------------------------------------------

   2 - access("PK"=1)

MySQL (FORMAT=TREE)

-> Rows fetched before execution
  • 実行フェーズの前の見積フェーズで行データにアクセスする「最適化」が実施されており、これを強調した表示になっているようだ。
  • FORMAT=TREEだと、表名、索引名すら表示されないのはどうなんだろうか…
    • ちなみに後述するFORMAT=TRADITIONALだと表示される

PostgreSQL

Index Scan using t1_pkey on t1
   Index Cond: (pk = 1)
  • Index Scanが、OracleのINDEX RANGE SCAN → TABLE ACCESS BY INDEX ROWID に相当するようだ

一意索引アクセス - 索引アクセス

SELECT * FROM t1 WHERE i1u=1;

Oracle

--------------------------------------------------
| Id  | Operation                   | Name       |
--------------------------------------------------
|   0 | SELECT STATEMENT            |            |
|   1 |  TABLE ACCESS BY INDEX ROWID| T1         |
|*  2 |   INDEX UNIQUE SCAN         | IDX_T1_I1U |
--------------------------------------------------

   2 - access("I1U"=1)

MySQL (FORMAT=TREE)

-> Rows fetched before execution
  • 実行フェーズの前の見積フェーズで行データにアクセスする「最適化」が実施されており、これを強調した表示になっているようだ。
  • FORMAT=TREEだと、表名、索引名すら表示されないのはどうなんだろうか…
    • ちなみに後述するFORMAT=TRADITIONALだと表示される

PostgreSQL

Index Scan using idx_t1_i1u on t1
   Index Cond: (i1u = 1)
  • Index Scanが、OracleのINDEX RANGE SCAN → TABLE ACCESS BY INDEX ROWID に相当するようだ

非一意索引アクセス - 索引アクセス

SELECT * FROM t1 WHERE i1u=1;

Oracle

----------------------------------------------------------
| Id  | Operation                           | Name       |
----------------------------------------------------------
|   0 | SELECT STATEMENT                    |            |
|   1 |  TABLE ACCESS BY INDEX ROWID BATCHED| T1         |
|*  2 |   INDEX RANGE SCAN                  | IDX_T1_I1X |
----------------------------------------------------------

   2 - access("I1X"=100)
  • 索引アクセスのオペレーションが"INDEX RANGE SCAN"であり、範囲検索である点が分かりやすい
    • 主キーアクセス、一意索引アクセスでは、オペレーションが"INDEX UNIQUE SCAN"だった

MySQL (FORMAT=TREE)

-> Index lookup on t1 using idx_t1_i1x (i1x=100)
  • Index lookupが、OracleのINDEX RANGE SCAN → TABLE ACCESS BY INDEX ROWID に相当するようだ

PostgreSQL

Index Scan using idx_t1_i1x on t1
   Index Cond: (i1x = 100)
  • Index Scanが、OracleのINDEX RANGE SCAN → TABLE ACCESS BY INDEX ROWID に相当するようだ

結合アルゴリズム

MySQLのEXPLAIN FORMAT

本セッションでは、以下の理由でEXPLAIN FORMAT=TREEを中心に説明したが、若干過渡的な状況(EXPLAIN FORMAT=TRADITIONAL → EXPLAIN FORMAT=TREE)にあるのかもしれない。

  • 他RDBMSに似た考え方で実行計画を表示できる
  • 結合やサブクエリの処理方法を理解しやすい
  • EXPLAIN ANALYZE (v8.0.18/2019-10~)はTREE表示形式のみに対応 → 今後は FORMAT=TREEが基本となる?

上: FORMAT=TRADITIONAL / 下: FORMAT=TREE

主キーアクセス

+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | t1    | NULL       | const | PRIMARY       | PRIMARY | 4       | const |    1 |   100.00 | NULL  |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
-> Rows fetched before execution

一意索引アクセス

+----+-------------+-------+------------+-------+---------------+------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type  | possible_keys | key        | key_len | ref   | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | t1    | NULL       | const | idx_t1_i1u    | idx_t1_i1u | 5       | const |    1 |   100.00 | NULL  |
+----+-------------+-------+------------+-------+---------------+------------+---------+-------+------+----------+-------+
-> Rows fetched before execution

非一意索引アクセス

+----+-------------+-------+------------+------+---------------+------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key        | key_len | ref   | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | t1    | NULL       | ref  | idx_t1_i1x    | idx_t1_i1x | 5       | const |    1 |   100.00 | NULL  |
+----+-------------+-------+------------+------+---------------+------------+---------+-------+------+----------+-------+
-> Index lookup on t1 using idx_t1_i1x (i1x=100)

上: FORMAT=TRADITIONAL / 下: FORMAT=TREE

ネステッドループ結合

+----+-------------+-------+------------+--------+--------------------------------------+------------+---------+---------------+------+----------+-------------+
| id | select_type | table | partitions | type   | possible_keys                        | key        | key_len | ref           | rows | filtered | Extra       |
+----+-------------+-------+------------+--------+--------------------------------------+------------+---------+---------------+------+----------+-------------+
|  1 | SIMPLE      | t3    | NULL       | ref    | idx_t3_i3x,idx_t3_i3y,idx_t3_i3x_i3y | idx_t3_i3y | 5       | const         |   45 |   100.00 | Using where |
|  1 | SIMPLE      | t1    | NULL       | eq_ref | idx_t1_i1u                           | idx_t1_i1u | 5       | testdb.t3.i3x |    1 |   100.00 | NULL        |
+----+-------------+-------+------------+--------+--------------------------------------+------------+---------+---------------+------+----------+-------------+
-> Nested loop inner join
  -> Filter: (t3.i3x is not null)
      -> Index lookup on t3 using idx_t3_i3y (i3y=1)
  -> Single-row index lookup on t1 using idx_t1_i1u (i1u=t3.i3x)

ハッシュ結合

+----+-------------+-------+------------+------+---------------------------+------------+---------+-------+------+----------+--------------------------------------------+
| id | select_type | table | partitions | type | possible_keys             | key        | key_len | ref   | rows | filtered | Extra                                      |
+----+-------------+-------+------------+------+---------------------------+------------+---------+-------+------+----------+--------------------------------------------+
|  1 | SIMPLE      | t2    | NULL       | ref  | idx_t2_i2x,idx_t2_i2x_i2y | idx_t2_i2x | 5       | const |  450 |   100.00 | NULL                                       |
|  1 | SIMPLE      | t1    | NULL       | ALL  | NULL                      | NULL       | NULL    | NULL  | 4654 |    10.00 | Using where; Using join buffer (hash join) |
+----+-------------+-------+------------+------+---------------------------+------------+---------+-------+------+----------+--------------------------------------------+
-> Inner hash join (t1.n1 = t2.n2)
  -> Table scan on t1
  -> Hash
      -> Index lookup on t2 using idx_t2_i2x (i2x=1)
  • FORMAT=TREEは結合操作の処理内容を直感的に理解しやすい

  • FORMAT=JSON は、情報量が多い反面、そのままでは読みにくい
    • → MySQL WorkbenchのVisual Explain機能などで図に整形して表示してから読むのが良い

Oracle SQL_IDとPlan hash value

SQL> SELECT * FROM t1 WHERE i1u=1;
SQL> SELECT * FROM table(DBMS_XPLAN.DISPLAY_CURSOR);

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------
SQL_ID  aqry97d8kxgas, child number 0
-------------------------------------
SELECT * FROM t1 WHERE i1u=1

Plan hash value: 2661144773

------------------------------------------------------------------------------------------
| Id  | Operation                   | Name       | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |            |       |       |     2 (100)|          |
|   1 |  TABLE ACCESS BY INDEX ROWID| T1         |     1 |  1018 |     2   (0)| 00:00:01 |
|*  2 |   INDEX UNIQUE SCAN         | IDX_T1_I1U |     1 |       |     1   (0)| 00:00:01 |
------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("I1U"=1)
  • SQL_ID: SQLの識別子(≒SQL文字列のハッシュ値)

  • Plan hash value: 実行計画の識別子

  • SQLおよび実行計画の同一性を簡単にチェックできるため、非常に便利

    • SQLや実行計画が長い場合にとても便利

ツリー構造のたどり方(実行計画の読み方)

  • 口頭説明の方が分かりやすいため、後述するセッション動画の視聴をお勧めします。

ステップ単位の統計値

  • 口頭説明の方が分かりやすいため、後述するセッション動画の視聴をお勧めします。

セッション資料のダウンロード

資料は、弊社の資料ダウンロードページ(要登録+DM配信同意)からダウンロードできます。

セッション動画の視聴

セッション動画は、db tech showcaseのアーカイブ配信サイトから視聴できますが、 db tech showcase 2024 Tokyoへの登録状況によって、視聴までの手順が異なる点に注意が必要です。

  • dbts2024登録済みの方(db tech showcase 2024 Tokyoへの参加申し込みをされた方、db tech showcase 2024 Tokyoアーカイブ配信事前登録をされた方)
  • dbts2024未登録の方(db tech showcase 2024 Tokyoへの参加申し込みおよびアーカイブ配信事前登録をされていない方)

私が確認した限り、セッション動画はYouTubeを使って配信されているようで、再生速度の調整や自動生成された字幕の表示が可能なようです。便利!

参考 「主要RDBMS製品の比較」について

「主要RDBMS製品の比較」ページ一覧

過去発表と発表資料

12/6 #dbts2023 「主要4種データベース製品(Oracle、MySQL、PostgreSQL、MS SQL Server)の相違点比較と異種データベース製品活用における課題と解決」セミナーのお知らせ

7/11 #dbts2024 「SQL実行計画 – 主要RDBMS製品の比較(Oracle, MySQL, PostgreSQL)」セミナーのお知らせ

  • バックアップと障害復旧から考えるOracle Database, MySQL, PostgreSQLの違い

バックアップと障害復旧から考えるOracle Database, MySQL, PostgreSQLの違い

資料は、弊社の資料ダウンロードページ(要登録+DM配信同意)からダウンロードできます。

立場の表明

  • コーソルはデータベース関連製品の販売およびプロフェッショナルサービス提供を行っている営利企業です。
  • https://cosol.jp にある全てのコンテンツは、情報提供に加えて、コーソルの認知度向上、コーソルの営利活動の促進を目的としています。

Database Performance Analyzer DPA

コーソルのDbvisitサービス

著者について

[PR] オンプレミス&クラウドのマルチDB製品に対応した性能管理ツールDPA

Database Performance Analyzer (DPA) は、オンプレミス&クラウドに対応するデータベース性能監視/分析ツールです。

Database Performance Analyzer DPA

この記事で取り上げたRDBMS製品を含む、非常に多くのデータベース製品/サービスに対応しています。

  • Oracle Database
  • MS SQL Server
  • Sybase SAP ASE
  • IBM Db2
  • MySQL / MariaDB / Percona Server for MySQL
  • PostgreSQL / Enterprise DB
  • AWS
    • Amazon RDS for Oracle Database / SQL Server / MySQL / MariaDB / PostgreSQL
    • Amazon Aurora for MySQL / PostgreSQL
  • Azure
    • Azure SQL Database
    • Azure SQL Managed Instance
    • Azure SQL for PostgreSQL
    • Azure Database for MySQL / MariaDB
  • Google Cloud
    • Google Cloud SQL for MySQL / PostgreSQL / SQL Server

以下の特徴があり、導入しやすく有用な製品です。

  • 非常に低価格。課金単位は監視インスタンスの数で、1インスタンス14.7万円/年から(2023年1月時点)。
  • インストールが容易。DBサーバへのエージェント導入は不要。
  • オンラインデモサイトですぐに使用感を確認可能 → https://cosol.jp/techdb/2022/08/dpa_online_demo/
  • 待機時間を基礎とする性能分析(近年主流の性能分析メソッド)
  • 機械学習アルゴリズムに基づく異常検知機能(Anomaly Detection)

なぜコーソルからDatabase Performance Analyzer (DPA)を購入すべきなのか

コーソルはDatabase Performance Analyzer (DPA)の一次代理店で、Database Performance Analyzer (DPA)の製品販売を行います。 SIer様、販社様がDatabase Performance Analyzer (DPA)を販売および導入することも可能です。

Database Performance Analyzer DPA

コーソルはデータベースの技術力を強みとしています。なかでもOracle Database技術力は日本随一です。MySQL、PostgreSQL、MS SQL Serverの資格や実績を持つエンジニアも多数在籍しております。

独自のDPAナレッジを公開

DPAの導入や監視設定に関する手順をナレッジとして公開しています。評価版をご利用される際の参考にしていただけると幸いです。

多数のOracle関連書籍を執筆

ORACLE MASTER Platinum取得者数 No.1

  • 単年度ORACLE MASTER Platinum取得者数7年連続No.1

7年連続ORACLE MASTER Platinum取得者数No.1! Oracle Certification Award 2020

[PR] コーソルのデータベース運用関連製品とサービス

コーソルでは、データベース運用を製品とサービスでご支援します。

Database Performance Analyzer (DPA)

Database Performance Analyzer (DPA)は、オンプレミスとクラウド上の多くのデータベース製品に対応したデータベース性能管理製品です。低価格であるため、非常に導入しやすいです。

自動SQLチューニング機能を持つToad

Database Performance Analyzer (DPA)で検出された問題SQLをチューニングする際に、Toad for Oracle / Toad for SQL Serverの SQL Optimizer機能を使用できます。

リモートDBAサービス

リモートDBAサービスはDB・運用の専門家がお客様のデータベースに対して 必要な時に必要な対応を行うリモート接続型運用保守サービスです。

データベース運用・保守なら常駐しないリモートDBA

時間制コンサルティングサービス

時間制コンサルティングサービスは”必要な時に” ”必要な時間だけ”契約できる 時間契約型のコンサルティングサービスです。

データベース コンサルティングなら時間制コンサルティング

プロフィール

On7tWW6m1Ul4

渡部 亮太

・Oracle ACE
・AWS Certified Solutions Architect - Associate
・ORACLE MASTER Platinum Oracle Database 11g, 12c 他多数

カテゴリー

アーカイブ