Sparkにおけるデータの偏り - 特定と対策

> **[DatabricksでSparkとDelta Lakeワークロードを最適化するための包括的ガイド](https://qiita.com/taka_yayoi/items/cd199691569fef069a9d)のコンテンツです。**

[Comprehensive Guide to Optimize Data Workloads \| Databricks](https://www.databricks.com/discover/pages/optimize-data-workloads-guide)のセクション[Data Skewness — Identification and Remediation](https://www.databricks.com/discover/pages/optimize-data-workloads-guide#data-skewness)の翻訳です。

:::note warn
- 本書は著者が手動で翻訳したものであり内容の正確性を保証するものではありません。正確な内容に関しては原文を参照ください。
- 2023年時点の内容です。一部情報が古いものがあります。
:::

[データの偏り](https://docs.databricks.com/en/archive/legacy/skew-join.html#skew-join-optimization)は、不均一なデータ分布によって、大規模データの処理が特定のCPUコアのみに偏ってしまう状況です。例えば、データが均等に分布していないカラムを用いてjoinや集計を行う際、完了に時間を要する(複数回の試行で失敗することもあります)偏りのあるシャッフルステージに陥ってしまいます。

# 偏りの特定

- シャッフルステージで全てのSparkタスクが完了し、それらの1つあるいは2つが長時間を要している場合、偏りを示しています。また、Spark UIからこの情報を得ることができます。

    ![](https://www.databricks.com/en-website-assets/static/6798158cd9b2e8706fbc2cb799add12c/22891.png)

- タスクサマリーのメトリクスで、シャッフルリードサイズの最小値と最大値に大きな開きがある場合、これもまたデータの偏りを示しています。

    ![](https://www.databricks.com/en-website-assets/static/272f282eb750fa7e3666930981797ffe/22893.png)

- シャッフルパーティションの数をファインチューニングした後でも多くのデータ溢れがある場合、偏りによる可能性があります。
- 最後になりますが、joinや集計カラムによるグルーピングを行う際にはシンプルに行数をカウントすることができます。行数に大きな隔たりがある場合、間違いなく偏りがあります。

    ```sql
    SELECT COLUMN_NAME,
    COUNT(*)
    FROM TABLE
    GROUP BY COLUMN_NAME
    ```

# 偏りの対策

## 1. 偏りのある値の除外

偏りがある場合そのような値を除外することができ、それによって容易に問題を解決するとができます。例えば、null値を大量に含むカラムを用いてjoinを行う際、データの偏りに直面することになります。このシナリオでは、null値を除外することでこの問題を解決できます。

## 2. 偏りのヒント

テーブル、カラムを特定でき、それらの値がデータの偏りを引き起こしているケースにおいては、Sparkが問題を解決しようと試みるように、[スキューヒント](https://learn.microsoft.com/ja-jp/azure/databricks/archive/legacy/skew-join)を用いて明示的にSparkに指示することができます。

```sql
SELECT /*+ SKEW(’table’, ’column_name’, (value1, value2)) */ * FROM table
```

## 3. AQEの偏りの最適化

また、Spark 3.0+で提供される[AQE](https://docs.databricks.com/ja/optimizations/aqe.html#dynamically-handle-skew-join)は、動的にデータの偏りを解決することができます。デフォルトで有効化されていますが、無効化したい場合には以下の設定をfalseに設定します:

```
set spark.sql.adaptive.skewJoin.enabled = false
```

デフォルトでは、全てのパーティションは最小でも256MBのデータサイズとなっており、平均パーティションサイズよりも5倍大きなパーティションはAQEとして偏りがあるものとみなされます。また、デフォルトのAQEの挙動をファインチューンすることもできます:

```
-- default is 5
set spark.sql.adaptive.skewJoin.skewedPartitionFactor = <value>
-- default is 256MB
set spark.sql.adaptive.skewJoin.skewedPartitionThresholdInBytes = <size in bytes>
```

## 4. ソルティング

上述のオプションがうまくいかない場合、残りのオプションはソルティングのみです。これは、偏りのあるカラムの値にサフィックスとしてランダムな整数値を追加することがで、偏りのある大規模なパーティションを小規模なパーティションに分割する戦略です。ソルティングのテクニックを用いてデータの偏りの問題を解決するためのサンプル[ノートブック](https://www.databricks.training/spark-ui-simulator/experiment-1596/v002-S/index.html)(Cmd 7以降)をご覧ください。このサンプルでは、joinクエリーでのデータ偏りを修正する方法を示しています。

偏りのある集計クエリーがあると想像してみましょう。データの偏りに対策するためにソルティングを活用することができますが、このような状況では、偏りに寄与している値に対する部分的なソルティングを実行することになります。あるeコマースの企業を考えてみましょう。全ての顧客における最新の取引日を特定したいと考えていますが、`customer_id`の値`xyz`の不均衡な分布によって、データが偏っています。このため、この偏りを修正するには、以下のように部分的なソルティングを実施します:

![](https://www.databricks.com/en-website-assets/static/eeed1c82a0acf117ec77b244dc2ebe2c/22974.png)

![](https://www.databricks.com/sites/default/files/inline-images/alert-icon.png?v=1688736447)

上述されている順番の通りに、これらのソリューションを試しましょう。これは、ソルティングはコードの変更を必要とするので、最初に行うのではなく最後の選択肢であることを意味します。ヒントやAQEのソリューションの方がはるかに実装がシンプルです。

### はじめてのDatabricks

[はじめてのDatabricks](https://qiita.com/taka_yayoi/items/8dc72d083edb879a5e5d)

### Databricks無料トライアル

[Databricks無料トライアル](https://databricks.com/jp/try-databricks)