Databricksにおけるデータファイル管理によるパフォーマンスの最適化

[Optimize performance with file management \| Databricks on AWS](https://docs.databricks.com/delta/optimizations/file-mgmt.html) [2022/6/2時点]の翻訳です。

:::note warn
本書は抄訳であり内容の正確性を保証するものではありません。正確な内容に関しては原文を参照ください。
:::

クエリーを高速化するために、Databricks上のDelta Lakeはクラウドストレージに格納されるデータのレイアウトの最適化をサポートしています。DatabricksのDelta Lakeは2つのレイアウトアルゴリズムをサポートしています。bin-packingとZ-Orderingです。

本書では、以下を説明します。

- 最適化コマンドをどのように実行するのか。
- 2つのレイアウトアルゴリズムがどのように動作するのか。
- 古いテーブルスナップショットをどのようにクリーンアップするのか。
- [FAQ](#faq)ではなぜ最適化が自動化されないのかを説明し、どの程度の頻度で最適化コマンドを実行すべきかの推奨を説明します。
- 最適化のメリットを説明するノートブックに関しては、[Optimization examples](https://docs.databricks.com/delta/optimizations/optimization-examples.html)を参照ください。
- Databricks SQL上のDelta Lakeの最適化コマンドのリファレンスに関しては、以下を参照ください。
    - Databricks Runtime 7.x以降: [OPTIMIZE \(Delta Lake on Databricks\)](https://docs.databricks.com/spark/latest/spark-sql/language-manual/delta-optimize.html)
    - Databricks Runtime 5.5 LTSおよび6.x: [Optimize \(Delta Lake on Databricks\)](https://docs.databricks.com/spark/2.x/spark-sql/language-manual/optimize.html)

# コンパクション(bin-packing)

DatabricksのDelta Lakeでは、小さなファイル群を大きなファイルにまとめることで、テーブルから読み込みを行うクエリーのスピードを改善することができます。`OPTIMIZE`コマンドを実行することでコンパクションを起動することができます。

```sql:SQL
OPTIMIZE delta.`/data/events`
```

```py:Python
from delta.tables import *
deltaTable = DeltaTable.forPath(spark, "/data/events")
deltaTable.optimize().executeCompaction()
```

```scala:Scala
import io.delta.tables._
val deltaTable = DeltaTable.forPath(spark, "/data/events")
deltaTable.optimize().executeCompaction()
```

あるいは、以下を実行します。

```sql:SQL
OPTIMIZE events
```

```py:Python
from delta.tables import *
deltaTable = DeltaTable.forName(spark, "events")
deltaTable.optimize().executeCompaction()
```

```scala:Scala
import io.delta.tables._
val deltaTable = DeltaTable.forPath(spark, "/data/events")
deltaTable.optimize().executeCompaction()
```

大規模データを保持しており、そのサブセットのみを最適化したい場合、オプションの`WHERE`を用いたパーティション述語を持ちいてサブセットを指定することができます。

```sql:SQL
OPTIMIZE events WHERE date >= '2022-11-18'
```

```py:Python
from delta.tables import *
deltaTable = DeltaTable.forName(spark, "events")
deltaTable.optimize().where("date='2021-11-18'").executeCompaction()
```

```scala:Scala
import io.delta.tables._
val deltaTable = DeltaTable.forName(spark, "events")
deltaTable.optimize().where("date='2021-11-18'").executeCompaction()
```

:::note
**注意**
- Bin-packingの最適化は冪等性が保証されるので、同じデータセットに対して2回実行したとしても、2回目の実行の効果はありません。
- Bin-packingは、ディスク上のデータファイルのサイズに基づいて、均等にバランスされたデータファイルを生成しようとしますが、ファイルごとのタプルの数が必ずしも均等になるわけではありません。しかし、これら二つの指標は多くの場合相関します。
- `OPTIMIZE`オペレーションのPythonとScala APIはDatabricksランタイム11.0以降で利用できます。
:::

Deltaテーブルのreaderはスナップショット分離を使用ており、`OPTIMIZE`がトランザクションログから不要なファイルを削除している際に影響を受けないことを意味します。`OPTIMIZE`はテーブルに対するデータの変更は行いませんので、`OPTIMIZE`の実行前後での読み取り結果は同じとなります。ストリーミングのソースとなっているテーブルに対する`OPTIMIZE`の実行は、このテーブルをソースとして取り扱うストリームの現状、未来の状態に影響を及ぼしません。`OPTIMIZE`はオペレーションによって削除・追加されたファイルに関する統計情報(min、max、totalなど)を返却します。OPTIMIZEの統計情報には、Z-Orderingの統計情報、バッチの数、最適化されたパーティションの数も含まれます。

:::note
**注意**
Databricksランタイム 6.0以降で利用できます。
:::

[Auto Optimize](https://docs.databricks.com/delta/optimizations/auto-optimize.html)を用いることで、小規模ファイルを自動でコンパクトにすることができます。

# データスキッピング

データスキッピングの情報は、Deltaテーブルにデータを書き込む際に自動で収集されます。DatabricksのDelta Lakeは、クエリーを行う際にこの情報(最小値、最大値)を活用してクエリーを高速化します。データスキッピングは設定不要です。利用できる場合には自動で適用されます。しかし、この効果はデータのレイアウトに依存します。ベストな結果を得るためには[Z-Ordering](#z-ordering多次元クラスタリング)を適用します。

Databricks Delta Lakeのデータスキッピング、Z-Orderingのメリットの例に関しては、[Optimization examples](https://docs.databricks.com/delta/optimizations/optimization-examples.html)のノートブックをご覧ください。Databricks Delta Lakeはデフォルトで、お使いのテーブルスキーマの最初の32列の統計情報を収集します。この値は、[テーブルプロパティ](https://docs.databricks.com/delta/delta-batch.html#table-properties)の`dataSkippingNumIndexedCols`で変更することができます。統計情報を収集するカラムを増やすと、ファイルを書き込む際のオーバーヘッドが増加します。

長い文字列の統計情報の収集はコストが大きいオペレーションです。長い文字列の統計情報の収集を回避するには、長い文字列を含むカラムを除外するためにテーブルプロパティ`dataSkippingNumIndexedCols`を設定するか、`ALTER TABLE ALTER COLUMN`を用いて、`dataSkippingNumIndexedCols`の範囲外の場所に長い文字列を含むカラムを移動するかします。以下を参照ください。

- Databricksランタイム 7.x以降: [ALTER TABLE](https://docs.databricks.com/spark/latest/spark-sql/language-manual/sql-ref-syntax-ddl-alter-table.html)
- Databricksランタイム 5.5 LTSと6.x: [Change columns](https://docs.databricks.com/spark/2.x/spark-sql/language-manual/alter-table-or-view.html#change-columns)

統計情報の収集のために、ネストされたカラムの個々のフィールドは個々のカラムと見なされます。

本書のトピックに関してはブログ記事[Databricks Deltaを使って秒でペタバイトデータを処理する](https://qiita.com/taka_yayoi/items/4bd090229b5b13a533f5)もご覧下さい。

# Z-Ordering(多次元クラスタリング)

Z-Orderingは関連する情報を同じファイルセットに配置する[テクニック](https://en.wikipedia.org/wiki/Z-order_curve)です。Databricks Delta Lakeのデータスキッピングアルゴリズムは読み取る必要があるデータ量を劇的に削減するために、自動でこの局所性を活用します。データをZ-Orderするには、`ZORDER BY`句に並び替えを行うカラムを指定します。

```sql:SQL
OPTIMIZE events
WHERE date >= current_timestamp() - INTERVAL 1 day
ZORDER BY (eventType)
```

クエリーの述語に一般的に用いられるカラムがわかっており、当該カラムのカーディナリティが高い場合(すなわち、異なる値の数が多い)には`ZORDER BY`を使ってください。

カンマ区切りで複数のカラムを`ZORDER BY`に指定することができます。しかし、カラムを追加するほど局所性の効果は減少します。データスキッピングはmin、max、countのようなコラム固有の統計情報を必要とするので、統計情報が収集されていないカラムに対するZ-Orderingは効果がなく、リソースの無駄となります。スキーマ内でカラムを並び替えることで統計情報を収集するカラムを指定するか、統計情報を収集する絡むの数を増やしてください。詳細は[データスキッピング](#データスキッピング)を参照ください。

:::note
**注意**
- Z-Orderingは*冪等性が保証されませんが*、インクリメンタルな処理を行おうとします。複数回のZ-Orderingで処理に要する時間が削減されることは保証されません。しかし、Z-Orderingが実行された直後のパーティションに新規データが追加されない場合、当該パーティションに対するさらなるZ-Orderingには効果がありません。
- Z-Orderingはタプルの数に基づいて均等なデータファイルを作成しようとしますが、ディスク上のデータファイルサイズが必ずしも均等になるわけではありません。これらの二つの指標は多くの場合相関しますが、そうでない場合もありえますし、最適化タスクの時間に偏りが発生することもあります。

    例えば、過去より幅の広い(例えば、より長い配列、文字列)最近のデータに対して、`ZORDER BY` *date*を実行した場合、`OPTIMIZE`のジョブタスクの時間、生成されるファイルサイズに偏りが生じることが予想されます。しかし、この場合、問題は`OPTIMIZE`コマンドに対するものであり、以降のクエリーにネガティブなインパクトはありません。
:::

# ファイルサイズのチューニング

このセクションでは、Deltaテーブルのサイズをどのようにチューニングするのかを説明します

## ターゲットサイズの設定

:::note
**注意**
Databricksランタイム8.2以降で利用できます。
:::

お使いのDeltaテーブルのサイズをチューニングしたい場合には、希望のサイズを[テーブルプロパティ](https://docs.databricks.com/delta/delta-batch.html#table-properties)の`delta.targetFileSize`に設定します。プロパティが設定されると、すべてのデータレイアウト最適化オペレーション(例えば、[コンパクション(bin-packing)](#コンパクションbin-packing)や[Z-Ordering](#z-ordering多次元クラスタリング)、[Auto Compaction](https://docs.databricks.com/delta/optimizations/auto-optimize.html#how-auto-compaction-works)、[Optimized Writes](https://docs.databricks.com/delta/optimizations/auto-optimize.html#how-optimized-writes-work))は指定されたサイズでファイルを生成するようにベストエフォートで処理を行います。

| テーブルプロパティ |
|:--|
| **delta.targetFileSize**<br>Type: バイトあるいはより大きい単位で指定するサイズ<br>ターゲットのファイルサイズ。例えば、`104857600` (バイト) あるいは `100mb`<br>デフォルト値: None  |

既存のテーブルに対しては、SQLコマンドの[ALTER TABLE SET TBL PROPERTIES](https://docs.databricks.com/sql/language-manual/sql-ref-syntax-ddl-alter-table.html)を用いて値の設定、解除が可能です。また、Sparkセッション設定を用いて、新規テーブルに自動的にこれらのプロパティを設定することができます。詳細は[Table properties](https://docs.databricks.com/delta/delta-batch.html#table-properties)を参照ください。

## ワークロードに基づく自動チューニング

:::note
**注意**
Databricksランタイム8.2以降で利用できます。
:::

手動でのチューニングの手間を最小化するために、Databricksではテーブルに操作を加えているワークロードに基づいて、Deltaテーブルのファイルサイズを自動でチューニングすることができます。Databricksはファイルの再書き込みを行う頻繁な`MERGE`を行うテーブルを自動で検知し、将来のさらなるファイルの再書き込みを予想して再書き込みを行うファイルサイズを小さくする選択をする場合があります。例えば、`MERGE`オペレーションを行う際、テーブルに対する過去の10回のオペレーションのうち9回がMERGEであった場合、`MERGE`で使用される[Optimized Writes](https://docs.databricks.com/delta/optimizations/auto-optimize.html#delta-optimized-writes)と[Auto Compaction](https://docs.databricks.com/delta/optimizations/auto-optimize.html#how-auto-compaction-works)が有効化されている場合、有効化されていない場合よりも小さいサイズのファイルを生成します。これによって、将来的な`MERGE`オペレーションの処理時間を削減する助けになります。

再書き込みが数回行われた後でオートチューニングが有効化されます。しかし、Deltaテーブルで頻繁な`MERGE`、`UPDATE`、`DELETE`オペレーションが行われることが予想され、即座にオートチューニングを有効化したい場合、テーブルプロパティ`delta.tuneFileSizesForRewrites`を設定することで、再書き込みに対するファイルサイズを明示的にチューニングすることができます。このプロパティを`true`に設定することで、当該テーブルに対する全てのデータレイアウト最適化オペレーションに対して常に小さい際ファイルサイズを使うようになるので、自動検知の起動が不要になります。

| テーブルプロパティ |
|:--|
| **delta.tuneFileSizesForRewrites**<br>Type: `Boolean`<br>データレイアウト最適化のファイルサイズをチューニングするかどうか。<br>デフォルト値: None  |

既存のテーブルに対しては、SQLコマンドの[ALTER TABLE SET TBL PROPERTIES](https://docs.databricks.com/sql/language-manual/sql-ref-syntax-ddl-alter-table.html)を用いて値の設定、解除が可能です。また、Sparkセッション設定を用いて、新規テーブルに自動的にこれらのプロパティを設定することができます。詳細は[Table properties](https://docs.databricks.com/delta/delta-batch.html#table-properties)を参照ください。

## テーブルサイズに基づく自動チューニング

:::note
**注意**
Databricksランタイム8.4以降で利用できます。
:::

手動でのチューニングの手間を最小化するために、Databricksではテーブルサイズに基づいてDeltaテーブルのファイルサイズを自動でチューニングすることができます。Databricksは、テーブルのファイル数が膨大にならないように、小さいテーブルには小さいファイルサイズを適用し、大きいテーブルには大きいファイルサイズを適用します。Dataricksは[ターゲットサイズの設定](#ターゲットサイズの設定)でチューニングしている場合、あるいは、頻繁な再書き込みを行うワークロードに基づくチューニングをしている場合にはこの自動チューニングを行いません。

現在のDeltaテーブルのサイズに基づいてターゲットファイルサイズが決定されます。2.56TBよりも小さいテーブルに対しては、自動チューニングされたターゲットファイルサイズは256MBとなります。2.56TBと10TBの間のテーブルに対しては、ターゲットファイルサイズは256MBから1GBに線形増加します。10TBより大きいテーブルでは、ターゲットファイルサイズは1GBになります。

:::note
**注意**
テーブルに対するターゲットファイルサイズが増加する際、`OPTIMIZE`コマンドによって既存のファイルは大きなサイズに再最適化されません。このため、大きなテーブルには常にターゲットサイズよりも小さいサイズのファイルがいくつか存在することになります。これらの小さなファイルも大きなファイルに最適化する必要がある場合には、テーブルプロパティ`delta.targetFileSize`を指定することで、テーブルに対する固定のターゲットファイルサイズを設定することができます。
:::

テーブルに対してインクリメンタルな書き込みが行われる際、ターゲットファイルサイズとファイル数はテーブルサイズに基づき以下のように近似されます。このテーブルのファイル数はあくまで例となります。実際の結果は、様々な要因によって変動します。

| テーブルサイズ | ターゲットファイルサイズ | テーブルのファイル数の近似値 |
|:--|:--|:--|
| 10GB  |256MB  | 40  |
| 1TB  | 256MB  |  4096 |
| 2.56TB  |  256MB | 10240  |
| 3TB  | 307MB  | 12108   |
| 5TB  | 512MB  | 17339  |
| 7TB  | 716MB  | 20784  |
| 10TB  | 1GB  |  24437 |
| 20TB  | 1GB  |  34437 |
| 50TB  | 1GB  | 64437  |
| 100TB  | 1GB  |  114437 |

# ノートブック

最適化のメリットを感じていただく例として、以下のノートブックを参照下さい。

- [Optimization examples](https://docs.databricks.com/delta/optimizations/optimization-examples.html)
    - [Delta Lake on Databricks optimizations Python notebook](https://docs.databricks.com/delta/optimizations/optimization-examples.html#delta-lake-on-databricks-optimizations-python-notebook)
    - [Delta Lake on Databricks optimizations Scala notebook](https://docs.databricks.com/delta/optimizations/optimization-examples.html#delta-lake-on-databricks-optimizations-scala-notebook)
    - [Delta Lake on Databricks optimizations SQL notebook](https://docs.databricks.com/delta/optimizations/optimization-examples.html#delta-lake-on-databricks-optimizations-sql-notebook)

Pythonのノートブックの翻訳版はこちらとなります。

https://github.com/taka-yayoi/public_repo/tree/main/delta_optimize

# インタラクティブクエリーの性能改善

Deltaエンジンは、クエリー性能を改善するためのいくつかの追加機能を提供しています。

- [データ鮮度の管理](#データ鮮度の管理)
- [低レーテンシークエリー向けに強化されたチェックポイント](#低レーテンシークエリー向けに強化されたチェックポイント)

## データ鮮度の管理

それぞれのクエリーの最初に、Deltaテーブルはテーブルの最新のバージョンまで自動で更新をかけます。このプロセスはノートブックのコマンドステータスレポートの`Updating the Delta table's state`で確認することができます。しかし、テーブルの過去のデータに対する分析を実施している場合、特にストリーミングで頻繁にデータが取り込まれている場合においては、分単位での最新データを必要としないかもしれません。このような場合、お使いのDeltaテーブルの古いスナップショットに対してクエリーが実行されます。これにより、クエリーの結果を得る際のレーテンシーを低減することができます。

Sparkセッション設定`spark.databricks.delta.stalenessLimit`に、`1h`(1時間)、`15m`(15分)、`1d`(1日)のように時間を表現する文字列を指定することで、お使いのテーブルデータがどのくらい古くて良いのかを設定することができます。この設定はセッション固有なので他のノートブック、ジョブ、BIツールなどからこのテーブルにアクセスする他のユーザーに影響を与えません。さらに、この設定はテーブルの更新を妨げるものではありません。ただ、クエリーはテーブルの更新を待つ必要があるだけです。バックグラウンドで更新は発生し、クラスターでリソースを公平に共有します。古さの制限を超えた場合、クエリーはテーブル状態の更新でブロックされます。

## 低レーテンシークエリー向けに強化されたチェックポイント

Delta Lakeは、集約されたDeltaテーブルの状態として[チェックポイント](https://github.com/delta-io/delta/blob/master/PROTOCOL.md#checkpoints)を最適化された頻度で書き込みます。これらのチェックポイントはテーブルの最新の状態を計算する際のスタート地点となります。チェックポイントがないと、Delta Lakeはテーブルの状態を計算するためにトランザクションログに対するコミットを表現するJSONファイル("delta"ファイル)の膨大なコレクションを読み込まなくてはなりません。さらに、[データスキッピング](#データスキッピング)を行うためにDelta Lakeが使用するカラムレベルの統計情報はチェックポイントに格納されます。

:::note warn
**重要！**
Delta Lakeのチェックポイントは[構造化ストリーミングのチェックポイント](https://qiita.com/taka_yayoi/items/6a563259261f42d24b2f#%E3%82%AF%E3%82%A8%E3%83%AA%E3%83%BC%E5%A4%B1%E6%95%97%E3%81%8B%E3%82%89%E3%81%AE%E3%83%AA%E3%82%AB%E3%83%90%E3%83%AA%E3%83%BC)とは別です。
:::

Databricksランタイム7.2以前では、カラムレベルの統計情報はDelta LakeのチェックポイントにJSONカラムとして保存されます。

Databricksランタイム7.3 LTS以降では、カラムレベルの統計情報はstructとして格納されます。structフォーマットにすることで、以下の理由からDelta Lakeはより高速に読み込みを行うことができます。

- カラムレベルの統計情報を取得するためにDelta Lakeが高コストなJSONパーシングを実行する必要がありません。
- Parquetのカラムプルーニング機能が、カラムの統計情報を読み込むために必要なI/Oを劇的に削減します。

structフォーマットを用いることで、Delta Lakeの読み込みオペレーションのオーバーヘッドを秒単位から十数ミリ秒に削減する最適化を可能とし、短時間のクエリーのレーテンシーを劇的に削減します。

### チェックポイントにおけるカラムレベル統計情報の管理

テーブルプロパティ`delta.checkpoint.writeStatsAsJson`と`delta.checkpoint.writeStatsAsStruct`を用いて、どのように統計情報をチェックポイントに書き込むのかを管理することができます。両方のプロパティが`false`に設定された場合、Delta Lakeはデータスキッピングを*行うことはできません*。

Databricksランタイム 7.3 LTS以降では:

- バッチの書き込み処理は統計情報をJSONとstructフォーマットの両方で書き込みます。`delta.checkpoint.writeStatsAsJson`は`true`です。
- デフォルトでは`delta.checkpoint.writeStatsAsStruct`はundefinedです。
- Readerは利用可能な場合にはstructカラムを使用し、そうでない場合にはJSONカラムにフォールバックします。

ストリーミング書き込みでは:

- Databricksランタイム 7.5以降: 統計情報をJSONフォーマット、structフォーマットの両方に書き込みます。
- Databricksランタイム 7.3 LTSと7.4: (チェックポイントによる書き込みレーテンシーへのインパクトを最小限にするために)統計情報をJSONフォーマットでのみ書き込みます。structフォーマットでも書き込みを行う際には、[チェックポイントにおける統計情報のトレードオフ](#チェックポイントにおける統計情報のトレードオフ)を参照ください。

Databricksランタイム 7.2以前では、ReaderはJSONカラムのみを使用します。このため、`delta.checkpoint.writeStatsAsJson`が`false`の場合、そのようなreaderはデータスキッピングを*行うことはできません*。

:::note warn
**重要！**
強化チェックポイントはオープンソースのDelta Lakeのreaderとの互換性を損なうものではありません。しかし、`delta.checkpoint.writeStatsAsJson`を`false`に設定した場合、プロプライエタリのDelta Lakeリーダーに影響を与える可能性があります。パフォーマンスへの影響に関してはベンダーにコンタクトください。
:::

### チェックポイントにおける統計情報のトレードオフ 

チェックポイントへの統計情報の書き込みにはコストがかかる(通常は大規模テーブルにおいても1分未満)ので、チェックポイントの書き込みに要する時間とDatabricksランタイム 7.2以下との互換性に対するトレードオフが存在します。お使いのワークロードをDatabricksランタイム 7.3 LTS以降にアップグレードすることができるのであれば、レガシーなJSONの統計情報を無効化することで、チェックポイントの書き込みコストを削減することができます。

お使いのアプリケーションでデータスキッピングが不要である場合、両方のプロパティをfalseに設定することで、統計情報が収集、書き込みされなくなります。この設定はお勧めしません。

<table>
<tr><td colspan=2 rowspan=2></td><td colspan=2>writeStatsAsStruct</td></tr>
<tr><td>false</td><td>true</td></tr>
<tr><td rowspan=2>writeStatsAsJson</td><td>false</td><td><ul><li>データスキップなし</li></ul></td><td><ul><li>Databricksランタイム7.3 LTS以降で高速なクエリーを実現</li><li>チェックポイント作成が若干遅くなる</li><li>Databricksランタイム 7.2以前ではreaderがデータスキッピングを利用できない</li></ul></td></tr>
<tr><td>false</td><td><ul><li>Databricksランタイム7.2以前</li><li>クエリーが遅い</li></ul></td><td><ul><li>Databricksランタイム7.3 LTS以降で高速なクエリーを実現</li><li>Databricksランタイム 7.2以前のreaderとの互換性を維持</li><li>チェックポイントのレーテンシーは最大(秒オーダー)</li></ul></td></tr>
</table>

### 構造化ストリーミングクエリーに対する強化チェックポイントの有効化

お使いの構造化ストリーミングのワークロードに低レーテンシーの要件がない場合、以下のSQLコマンドを実行することで強化チェックポイントを有効化することができます。

```sql:SQL
ALTER TABLE [<table-name>|delta.`<path-to-table>`] SET TBLPROPERTIES
('delta.checkpoint.writeStatsAsStruct' = 'true')
```

データにクエリーを実行するためにDatabricksランタイム 7.2以前を使用していない場合には、以下のテーブルプロパティを設定することでチェックポイントのレーテンシーの書き込み性能を改善することができます。

```sql:SQL
ALTER TABLE [<table-name>|delta.`<path-to-table>`] SET TBLPROPERTIES
(
 'delta.checkpoint.writeStatsAsStruct' = 'true',
 'delta.checkpoint.writeStatsAsJson' = 'false'
)
```

### 統計情報のstructなしにチェックポイントを書き込むクラスターからの書き込みを無効化する

Databricksランタイム 7.2以前のwriterは、統計情報のstructなしにチェックポイントを書き込みますが、これはDatabricksランタイム 7.3 LTSのreaderに対する最適化を妨げます。

Databricksランタイム 7.2以前が実行されているクラスターによるDeltaテーブルへの書き込みをブロックするために、`upgradeTableProtocol`メソッドを用いてDeltaテーブルをアップグレードすることができます。

```py:Python
from delta.tables import DeltaTable
delta = DeltaTable.forPath(spark, "path_to_table") # or DeltaTable.forName
delta.upgradeTableProtocol(1, 3)
```

```scala:Scala
import io.delta.tables.DeltaTable
val delta = DeltaTable.forPath(spark, "path_to_table") // or DeltaTable.forName
delta.upgradeTableProtocol(1, 3)
```

:::note warn
**警告！**
`upgradeTableProtocol`メソッドを適用することで、お使いのテーブルに対するDatabricksランタイム 7.2以前のクラスターの書き込みはできなくなり、この変更を戻すことはできません。新たなフォーマットに切り替えることをコミットしたあとでのみ、テーブルをアップグレードすることをお勧めします。Databricksランタイム 7.3 LTSを用いてテーブルのシャロー[CLONE](https://docs.databricks.com/spark/latest/spark-sql/language-manual/delta-clone.html)を作成することで、これらの最適化をトライすることができます。
:::

テーブルwriterのバージョンをアップグレードしたら、writerは`'delta.checkpoint.writeStatsAsStruct'`と` 'delta.checkpoint.writeStatsAsJson'`の設定に従わなくてはなりません。

以下のテーブルでは様々なバージョンのDatabricksランタイム、プロトコルのバージョン、wrtiterのタイプに応じて強化チェックポイントをどのように活用できるのかをまとめています。

:::note
**注意**
テーブルは[原文](https://docs.databricks.com/delta/optimizations/file-mgmt.html#disable-writes-from-clusters-that-write-checkpoints-without-the-stats-struct)を参照ください。
:::

### 古いチェックポイントフォーマットを使用するクラスターの書き込みを無効化する

Databricksランタイム 7.2以前のwriterは古いフォーマットでチェックポイントを書き込むことができ、これはDatabricksランタイム 7.3 LTSのwriterの最適化を妨げます。Databricksランタイム 7.2以前が実行されているクラスターによるDeltaテーブルへの書き込みをブロックするために、`upgradeTableProtocol`メソッドを用いてDeltaテーブルをアップグレードすることができます。

```py:Python
from delta.tables import DeltaTable
delta = DeltaTable.forPath(spark, "path_to_table") # or DeltaTable.forName
delta.upgradeTableProtocol(1, 3)
```

```scala:Scala
import io.delta.tables.DeltaTable
val delta = DeltaTable.forPath(spark, "path_to_table") // or DeltaTable.forName
delta.upgradeTableProtocol(1, 3)
```

:::note warn
**警告！**
`upgradeTableProtocol`メソッドを適用することで、お使いのテーブルに対するDatabricksランタイム 7.2以前のクラスターの書き込みはできなくなり、この変更を戻すことはできません。新たなフォーマットに切り替えることをコミットしたあとでのみ、テーブルをアップグレードすることをお勧めします。Databricksランタイム 7.3 LTSを用いてテーブルのシャローCLONEを作成することで、これらの最適化をトライすることができます。

- Databricksランタイム 7.x以降: [CREATE TABLE CLONE](https://docs.databricks.com/spark/latest/spark-sql/language-manual/delta-clone.html)
- Databricksランタイム 5.5 LTSおよび6.x: [Clone \(Delta Lake on Databricks\)](https://docs.databricks.com/spark/2.x/spark-sql/language-manual/clone.html)
:::

# FAQ

**なぜ`OPTIMIZE`は自動ではないのか？**

`OPTIMIZE`オペレーションは、コンパクション(オプションとしてZ-Orderingの実行)を通じてファイルのサイズを最適化するために多くのSparkジョブを起動します。`OPTIMIZE`が実行することの大部分は小規模ファイルの*コンパクション*なので、オペレーションが効果を発揮する前にまず初めに大量の小規模ファイルを集約する必要があります。このため、`OPTIMIZE`オペレーションを実行します。

さらに、`OPTIMIZE`の実行、特に`ZORDER`は時間、リソースの観点で高コストなものになります。Databricksが`OPTIMIZE`を自動で実行し、バッチにおいてデータの書き込みを待つことになる、低レーテンシーの(Deltaテーブルがソースとなる)Delta Lakeストリームを実行する能力を損なうことになります。多くのお客様においては、テーブルからデータをストリーミングしていることから、`OPTIMIZE`が提供するであろうクエリーのメリットがあったとしても、決してDeltaテーブルの最適化を実施していません。

最後に、(`OPTIMIZE`オペレーションを経由しようがしまいが)Delta Lakeはテーブルに書き込まれたファイルに関する統計情報を自動で収集します。これは、Detlaテーブルからの読み込みにおいては、テーブルやパーティションに`OPTIMIZE`オペレーションを実行したかどうかにかかわらず、この情報を活用することを意味します。


**どのくらいの頻度で`OPTIMIZE`を実行すべきですか？**

`OPTIMIZE`の実行頻度を検討する際、パフォーマンスとコストのトレードオフが存在します。すぐれたエンドユーザーのクエリーパフォーマンス(リソースの使い方によっては、より高いコストでも必要となります)を必要とするのであれば、高い頻度で`OPTIMIZE`を実行すべきです。コストを最適化したい場合には頻度を下げて実行すべきです。

日次ベース(スポット価格の安い夜間の実行をお勧めします)で`OPTIMIZE`を実行するところからスタートすることをお勧めします。その後に更新頻度を更新していきます。

**`OPTIMIZE`を実行するのに最適なインスタンスタイプは何ですか？**

両方のオペレーションは、大量のParquetデコーディング、エンコーディングを行うCPUを必要とする処理となります。

これらのワークロードに対しては、c5dシリーズをお勧めします。

### Databricks 無料トライアル

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