Delta Lakeの削除ベクトル(Deletion Vector)について

この記事は[Databricks Advent Calendar 2023](https://qiita.com/advent-calendar/2023/databricks)の12月14日分として投稿するものです。
## 削除ベクトルの概要

削除ベクトル(Deletion Vector)はDelta Lakeのストレージ最適化のための新しい技術であり、Delta Lake 2.4以降 (Databricks Runtime 13.2以降)でDeltaテーブルに対して適用可能になっています。

削除ベクトルとは何か？どのように使えばいいかについて解説します。

## 削除ベクトルの仕組みとメリット

### Delta Lakeの更新/削除の際の一般的な挙動
Delta Lakeではデータの更新/削除の際、元ファイルを上書きすることなく、新しいファイルを作成します。つまりParquetファイルから1行でも削除する時は、そのParquetファイルの残りのデータを新しいファイルとして出力することになります。以下のイメージ図の上側の流れになります。
(参照：[Delta Transaction Log Protocol | Delta Lake](https://github.com/delta-io/delta/blob/master/PROTOCOL.md#overview) )

### 削除ベクトルが適用されたDeltaテーブル
一方、削除ベクトルが適用されたDeltaテーブルの場合、新ファイルを作成することなく、削除対象行を表す削除ベクトルファイルが作成されます。以下のイメージ図の下側の流れになります。

![スクリーンショット 2023-12-18 12.59.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2663175/08de8ec2-41c5-71c7-c856-730a6c166286.png)


### 削除ベクトルのメリット
- 削除ベクトルを適用することで、削除時にParquetファイルを新規作成する必要がなくなるため、削除が複数のパーティションにまたがり出力するParquetが大量になる場合に短時間で済むというメリットがあります。
- 削除ベクトルが適用されたDeltaテーブルは、DBRが14.2以降でPhotonが有効など一定の条件を満たすと[行レベルのコンカレンシーが有効になります](https://docs.databricks.com/ja/optimizations/isolation-level.html#write-conflicts-with-row-level-concurrency)。

## 削除ベクトルの適用

- テーブル作成時だけでなく、既存テーブルに対しても削除ベクトルを適用可能です。
(DBR14.2ではデフォルトで非適用です)
```SQL
CREATE TABLE <table-name> [options] TBLPROPERTIES ('delta.enableDeletionVectors' = true);

ALTER TABLE <table-name> SET TBLPROPERTIES ('delta.enableDeletionVectors' = true);
```

- ワークスペースの設定を変更することで、削除ベクトルをデフォルトで有効にすることもできます。詳しくは以下のページを参照ください。
(参考：[削除ベクトルの自動有効化 | databricks](https://docs.databricks.com/ja/administration-guide/workspace-settings/deletion-vectors.html)）

## 削除ベクトルを使ってみる

実際に、削除ベクトル非適用、削除ベクトル適用のDeltaテーブルを作成して削除するとどう違うのかを試してみました。DBR14.2を使いました。実際のノートブックは[githubに置いてあります](https://github.com/motokazu-ishikawa-db/public_repo/blob/main/20231214_deletion_vector.py)。

### 1　テーブル作成とデータ投入

以下がテーブル作成時のSQLで、SQL最下段のTBLPROPERTIESのところで設定します。上の非適用のテーブルではfalse、下の適用のテーブルではtrueに設定しました。
```SQL
CREATE OR REPLACE TABLE $catalog_name.$schema_name.$table_dv_false (
  date INTEGER NOT NULL,
  delay INT NOT NULL,
  distance INT NOT NULL,
  origin STRING NOT NULL,
  destination STRING NOT NULL
)
LOCATION '$external_location_path/$table_dv_false'
TBLPROPERTIES ('delta.enableDeletionVectors' = false);
```
```SQL
CREATE OR REPLACE TABLE $catalog_name.$schema_name.$table_dv_true (
  date INTEGER NOT NULL,
  delay INT NOT NULL,
  distance INT NOT NULL,
  origin STRING NOT NULL,
  destination STRING NOT NULL
)
LOCATION '$external_location_path/$table_dv_true'
TBLPROPERTIES ('delta.enableDeletionVectors' = true);
```
その後、初期データを投入しました（コードは省略)。

### 2　削除前のファイル

削除前のファイルの状況をLISTコマンドで見てみると以下の通りです。上が非適用、下が適用のテーブルで、それぞれParquetファイルは1つずつです。
![スクリーンショット 2023-12-18 11.51.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2663175/3a85892e-c00b-a604-54fe-defc6bb99d90.png)
![スクリーンショット 2023-12-18 11.51.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2663175/329eee57-81ed-85ca-a7e1-bc0e03ce7edd.png)

### 3 データ削除

以下のDELETEコマンドでそれぞれ複数行を削除しました。

```SQL
DELETE FROM <table name> WHERE origin = 'ATL' AND destination = 'IAD';
```

### 4-1 削除ベクトル非適用の場合の削除後のファイル

データ削除後の削除ベクトル非適用のDeltaテーブルのファイルの様子です。このように、旧Parquetファイルはそのままで、それとは別に新しいParquetファイルが出来ています。
![スクリーンショット 2023-12-18 11.53.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2663175/d8ce707f-2eb9-ec3c-f8df-fca65b01e21f.png)


### 4-2 削除ベクトル適用の場合の削除後のファイル

データ削除後の削除ベクトル非適用のDeltaテーブルのファイルの様子です。新Parquetファイルが作られる代わりに、削除ベクトルファイルが作られています。
![スクリーンショット 2023-12-18 11.55.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2663175/843a360c-f1f9-3ac8-a5ce-2d087d939de7.png)