すべてのプロダクト
Search
ドキュメントセンター

PolarDB:SQLクエリ最適化ルールとSQL実行プラン

最終更新日:May 24, 2024

このトピックでは、PolarDB-XがSQL文の処理に使用する基本的な最適化の原則と実行計画について説明します。

分散データベースは、データベースアーキテクチャのスタンドアロンデータベースとは異なります。 スタンドアロンデータベースでSQLクエリを最適化するために使用されるメソッドは、分散データベースでSQLクエリを最適化するように変更できます。 PolarDB-Xを使用すると、統計、実行計画、並列処理の程度を定義するルール、およびSQLクエリの実行に費やされた報告時間に基づいて、低速SQLクエリの原因を特定できます。 これにより、ビジネスシナリオに基づいてSQLクエリを最適化できます。

低速クエリは、物理SQLクエリの実行が遅い場合、過剰な数のクエリが並列に実行される場合、または不適切なクエリ実行プランまたはインデックスが使用される場合に発生する可能性があります。 したがって、分散データベースでのSQLクエリ最適化のコストは、スタンドアロンデータベースでのSQLクエリ最適化のコストよりも高くなります。

基本原則

PolarDB-Xは、コンピューティングがストレージから切り離された分散データベースサービスです。 PolarDB-Xに送信するSELECTクエリは、論理SQLクエリです。 SELECTクエリがPolarDB-Xの計算ノードに送信されると、PolarDB-Xは、サブクエリをプッシュダウンできるかどうかに基づいて、SELECTクエリのサブクエリを2つのタイプに分類します。 プッシュダウンできるサブクエリは、物理SQLクエリと呼ばれます。 プッシュダウンできないSQLクエリは、計算ノードで実行されます。 プッシュダウン可能なSQLクエリは、データノードで実行されます。

PolarDB-X optimizes SQL queries based on the following rules:
  • PolarDB-X pushes down as many logical SQL queries as possible and runs these queries on data nodes. This reduces the network latency between data nodes and compute nodes. The SQL queries that are pushed down can be run on database shards in parallel. This way, the resources of each data node are used in an efficient manner and the SQL queries are accelerated.
  • The optimizer of PolarDB-X selects an optimal method to execute the physical operators that cannot be pushed down. For example, the optimizer selects the physical operators and rules that define the degree of parallelism based on your business scenario, and determines whether to run SQL queries in massively parallel processing (MPP) mode.
    説明 Degree of parallelism: the maximum number of queries that can be processed by PolarDB-X in parallel. For compute nodes, the degree of parallelism indicates the parallel processing capability of CPUs that have multiple cores. This capability determines the maximum number of threads that can be run by the compute nodes in parallel. For data nodes, the degree of parallelism indicates the maximum number of physical SQL queries that can be pushed down and run in parallel.
  • Local indexes and global indexes are used in PolarDB-X. A local index is created on a single data node. The local index is also knows as the MySQL index. A global index is a distributed index that is created on multiple data nodes. An index that is suitable for your business scenario can improve the query performance of your PolarDB-X instance.

実行プラン

SQL文がPolarDB-X分散データベースに送信されて実行されると、PolarDB-XはSQL文を解析して最適化し、実行計画を生成します。 実行プランは、演算子間の依存関係に基づいて生成されます。 実行プランツリーを表示することで、データベースでのSQL文の実行方法に関する情報を取得できます。 次のサンプルコードでは、実行計画の例を示します。

  • 例1

    次のSQL文を実行します。

    EXPLAIN select count(*) from lineitem group by L_LINESTATUS;

    実行プランに関する次の情報が返されます。

    HashAgg(group="L_LINESTATUS", count(*)="SUM(count(*))")                                                                                                                              
         Exchange (配布=hash[0], collation=[])                                                                                                                                       
           LogicalView(tables="[000000-000003].lineitem_[00-15]", shardCount=16, sql="SELECT 'L_LINESTATUS', COUNT(*) AS 'count(*)'FROM 'lineitem' AS 'lineitem' GROUP BY 'L_LINESTATUS'") 

    GROUP BY句の内容とテーブルのlineitemパーティションキーが一致しないため、GROUP BY操作はストレージ層で完了できません。 この場合、GROUP BY操作は2つのフェーズに分割されます。 パーティション集約操作は、部分集約を実行するためにストレージ層にプッシュダウンされる。 集計が実行された後、データは計算レイヤーにプッシュされます。 計算層では、データが再分配され、集約される。 次に、集約されたデータがクライアントに返される。

    • LogicalView: 16個のシャードがスキャンされることを指定します。 したがって、複数の物理SQL文が生成され、ストレージ層にプッシュダウンされます。 各物理SQLステートメントには、ストレージ層でデータを集計するために実行されるGROUP BY句が含まれています。
    • Exchange: LogicalView演算子によって返されたデータを集計し、L_LINESTATUSフィールドに基づいてデータを下流の演算子に再配布します。
    • HashAgg: 複数の入力データセットを受け取り、データを集約します。
  • 例2

    次のSQL文を実行します。

    EXPLAIN select * from lineitem, orders where L_ORDERKEY= O_ORDERKEY;

    実行プランに関する次の情報が返されます。

    +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
     HashJoin(condition="O_ORDERKEY = L_ORDERKEY", type="inner")                                                                                                                                                                                                                                                                                                      
       Exchange (配布=hash[0], collation=[])                                                                                                                                                                                                                                                                                                                   
         LogicalView(tables="[000000-000003].lineitem_[00-15]" 、shardCount=16、sql="SELECT 'L_ORDERKEY' '、'L_SUPPKEY' 、'L_LINENUMBER' 、'L_QUTENDETだ '、'LINAT'' '' '' 、'COML_ITEL' 、'' '、' '' '' '' '_ITEL' 、' '、' '、' '' '' '' '' '' '_'L_SHIPINSTRUCT' 、'L_SHIPMODE' 、'L_COMMENT' FROM 'lineitem' AS 'lineitem'") 
       Exchange (配布=hash[0], collation=[])                                                                                                                                                                                                                                                                                                                   
         LogicalView(tables="[000000-000003].orders_[00-15]" 、shardCount=16、sql="SELECT 'O_ORDERKEY' '、'O_ORDERPRIOSTATUS' 、'O_TOTALPRICE' 、'O_ORDERPRIPORIOTY' 、''_ COMCLE'_TY'IPO' 
    ' 、'_ CODERA' 、'、'SIPO' 、'_COREOMENCO_TYOMENOREO
    この例では、クエリは2つのテーブルを結合します。 2つのテーブルのパーティションキーに一貫性がありません。 この場合、ジョイン操作は、実行される記憶層にプッシュダウンされない。 記憶層では、両方のテーブルがスキャンされる。 結合演算は、計算層で実行される。
    • LogicalView: テーブルからデータをスキャンします。
    • Exchange: LogicalView演算子によって返されたデータを集計し、結合条件が作成された列に基づいてデータを下流の演算子に再分配します。
    • HashJoin: 2つのテーブルからデータを受け取り、HashTableメソッドを使用して受け取ったデータを結合します。
  • 例3

    次のSQL文を実行します。

    EXPLAIN select * from lineitem, orders where L_LINENUMBER= O_ORDERKEY;

    実行プランに関する次の情報が返されます。

    Gather(concurrent=true)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       
       LogicalView(tables="[000000-000003].lineitem_[00-15],orders_[00-15]", shardCount=16, sql="SELECT 'lineitem '.'L_ORDERKEY'', 'lineitem '.'L_PARTKEY'', '_ DPIINTEM'.''. '''_ DISEL', '' '' ', ' '' '' '' '' '' '' '' '' '' '' '' '' ''lineitem'.'L_TAX' 、'lineitem'. 'L_RETURNELAG' 、'L_LINCTRAT' 、'lineitem'.'L_SHIPDATE' 、'lineitem'. 'L_COMMITDAT'' 、'L_RECESTAT' '' '、'KEm'. '''MENEm'_ライン' 、'' '' '' ''''''LINEMENEm_SHL' '' '' '' '' '、' '、' 'orders'.'O_ORDERDATE' 、'orders'.'O_ORDERPRIORITY' 、'orders' 、'orders'.'O_SHIPPRIORITY' 、'orders'. 'ORMENT' FROM 'From'''''''lineiteor''_'''''ENNER JOorder'' 
    ''ines'''''ines', ''''''inesorder'' (例) 2つのテーブルの指定されたパーティションキーは一致しています。 この場合、結合操作は、実行されるストレージレイヤの指定されたシャードにプッシュダウンされます。 計算層では、Gather演算子を使用して、ストレージ層から返されるデータを計算します。
  • 例4

    次のSQL文を実行します。

    EXPLAIN select * from gsi_dml_unique_multi_index_base、integer_test=1;

    実行プランに関する次の情報が返されます。

    +-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
     プロジェクト (pk="pk" 、integer_dou_test="integer_test" 、varchar_test="varchar_test" 、char_test="char_test" 、blob_test="blob_test" 、tinyint_1bit___test="tinyint_test=" 、tinyint_flot_t_t_int_gint_gint_int_gint_gint_gint_test=="、" 、「medi_int_int_ decimal_test="decimal_test" 、date_test="date_test" 、time_test="time_test" 、datetime_test="datetime_test" 、timestamp_test="timestamp_test" 、year_test="year_test" 、mediumtext_test="mediumtext_test") 
       BKAJoin(condition="pk = pk", type="inner")                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          
         IndexScan(tables="DRDS_POLARX1_QATEST_APP_000000_GROUP.gsi_dml_unique_multi_index_index1_a0ol_01", sql="SELECT 'pk', 'integer_test', 'varchar_test', 'char_test', 'bit_test', 'bigint_test', 'double_test', 'date_test' FROM 'gsi_dml_unique_multix_integer_'W_indeg_inde_inege_inege_? ')' (_) ''''e_index') '?'                                                                                                                                                                                                                              
         収集 (concurrent=true)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           
           LogicalView(tables="[000000-000003].gsi_dml_unique_multi_index_base_[00-15]" 、shardCount=16、sql="SELECT 'pk' 、'blob_test' 、'tinyint_test' '、'mediumint_1_test' 、'_test'_test' 、'_test' 年、'、'___test' 、'、'_______test' 、'' 、''_test' 'mediumtext_test' FROM 'gsi_dml_unique_multi_index_base 'AS 'gsi_dml_unique_multi_index_base' WHERE (('integer_test' = ?) AND ('pk' IN (...))")                                                                                                                 
     HitCache:true
    この例では、SQLステートメントは述語のみを含み、単純なクエリを定義します。 SQL文の実行計画では、BKAJoin演算子を使用して2つのテーブルを結合することが示されます。 これは、gsi_dml_unique_multi_index_baseテーブルには、integer_test列に作成されたグローバルセカンダリインデックスがあるためです。 クエリがインデックスにヒットした場合、クエリされたデータをスキャンするために消費されるリソースのオーバーヘッドを減らすことができます。 インデックスはカバーインデックスではありません。 この場合、ベーステーブルもスキャンされます。
    • IndexScan: integer_test=1の条件に基づいてgsi_dml_unique_multi_index_index1_a0ol_01 indexテーブルをスキャンします。
    • BKAJoin: IndexScan演算子によって返されたデータを収集し、インデックステーブルとgsi_dml_unique_multi_index_baseベーステーブルを結合して、インデックステーブルから取得されていないデータをスキャンします。
    説明 ほとんどの場合、EXPLAINステートメントを使用して、SQLステートメントの実行計画を照会し、ステートメントの実行方法に関する情報を表示できます。 たとえば、グローバルセカンダリインデックスがヒットしたかどうかを確認できます。 実行するストレージ層にプッシュダウンされたSQL句の場合、EXPLAIN EXECUTEステートメントを使用して、ストレージ層で実行される物理SQLステートメントに関する情報を照会できます。 たとえば、指定されたテーブルのローカルインデックスがヒットしたかどうかを確認できます。