スキューヒントを使用したスキュー結合の最適化

この記事では、スキュー ヒントを使用して、クエリのパフォーマンスを低下させる可能性のある条件であるテーブル内のデータ スキューを改善する方法について説明します。

スキュー結合のヒントは必要ありません。 アダプティブクエリー実行 (AQE) と spark.sql.adaptive.skewJoin.enabled の両方が有効になっている場合、スキューは自動的に処理されます。 アダプティブクエリー実行を参照してください。

データスキューとは

データ スキューは、テーブルのデータがクラスター内のパーティション間で不均等に分散されている状態です。 データ スキューは、クエリー (特に結合を伴うクエリー) のパフォーマンスを大幅に低下させる可能性があります。 大きなテーブル間の結合にはデータのシャッフルが必要であり、スキューによってクラスター内の作業が極端に不均衡になる可能性があります。 クエリーが非常に少ないタスク (たとえば、200 のうち最後の 3 つのタスク) を完了し滞っているように見える場合は、データ スキューがクエリーに影響を与えている可能性があります。 データ スキューがクエリーに影響を与えていることを確認するには、次の手順を実行します。

  1. スタックしているステージをクリックし、結合を実行していることを確認します。

  2. クエリーが終了したら、結合を実行するステージを見つけて、タスク期間の分布を確認します。

  3. 期間を短くしてタスクを並べ替え、最初のいくつかのタスクを確認します。 1 つのタスクの完了に他のタスクよりもはるかに長い時間がかかった場合は、スキューが発生します。

スキューを改善するために、Databricks SQL の Delta Lake はクエリーで スキュー ヒント を受け入れます。 スキューヒントからの情報を使用して、 Databricks Runtime は、データスキューの影響を受けない、より優れたクエリプランを構築できます。

リレーション名 でスキューヒントを設定する

スキューヒントには、少なくともスキューのあるリレーションの名前が含まれている必要があります。 リレーションは、テーブル、ビュー、またはサブクエリです。 このリレーションを持つすべての結合では、スキュー結合の最適化が使用されます。

-- table with skew
SELECT /*+ SKEW('orders') */
  *
  FROM orders, customers
  WHERE c_custId = o_custId

-- subquery with skew
SELECT /*+ SKEW('C1') */
  *
  FROM (SELECT * FROM customers WHERE c_custId < 100) C1, orders
  WHERE C1.c_custId = o_custId

リレーション名とカラム名 でスキューヒントを設定する

リレーションに複数の結合があり、そのうちの一部だけがスキューの影響を受ける可能性があります。 スキュー結合の最適化にはオーバーヘッドがあるため、必要な場合にのみ使用することをお勧めします。 この目的のために、スキューヒントは列名を受け入れます。 これらの列との結合のみが、スキュー結合の最適化を使用します。

-- single column
SELECT /*+ SKEW('orders', 'o_custId') */
  *
  FROM orders, customers
  WHERE o_custId = c_custId

-- multiple columns
SELECT /*+ SKEW('orders', ('o_custId', 'o_storeRegionId')) */
  *
  FROM orders, customers
  WHERE o_custId = c_custId AND o_storeRegionId = c_regionId

リレーション名、列名、スキュー値 を使用したスキューヒントの設定

ヒントでスキュー値を指定することもできます。 クエリーとデータによっては、スキュー値がわかっている場合 (たとえば、変更されないため)、簡単に見つけられる場合があります。 これにより、スキュー結合の最適化のオーバーヘッドが削減されます。 それ以外の場合、 Delta Lake は自動的にそれらを検出します。

-- single column, single skew value
SELECT /*+ SKEW('orders', 'o_custId', 0) */
  *
  FROM orders, customers
  WHERE o_custId = c_custId

-- single column, multiple skew values
SELECT /*+ SKEW('orders', 'o_custId', (0, 1, 2)) */
  *
  FROM orders, customers
  WHERE o_custId = c_custId

-- multiple columns, multiple skew values
SELECT /*+ SKEW('orders', ('o_custId', 'o_storeRegionId'), ((0, 1001), (1, 1002))) */
  *
  FROM orders, customers
  WHERE o_custId = c_custId AND o_storeRegionId = c_regionId