Databricks ( Spark ) における Spark テーブル（データレイク）のディレクトリ構成の検討

## 概要
Databricks ( Spark ) におけるデータレイクのディレクトリ構成を、Azure のクラウド導入フレームワーク（以後、Azure CAF）をベースにした検討案を共有します。

## データアーキテクチャの方針

データアーキテクチャとして、３つのレイヤーで管理を行うメダリオンアーキテクチャをベースに実装することを前提とします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/bdd33690-88f7-fd32-2a8c-4b9112bc926f.png)

引用元：[メダリオンアーキテクチャ | Databricks](https://www.databricks.com/jp/glossary/medallion-architecture)

メダリオンアーキテクチャをベースに、マイクロソフトが提唱しているマルチホップアーキテクチャで詳細化をします。マイクロソフトが提唱しているアーキテクチャをそのまま利用してもよいのですが、Bronze -> Silver -> Gold という順に処理を行うと整理したほうがわかりやすいため、メダリオンアーキテクチャをベースにすることとしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/ba67fa7f-1e66-50b1-0086-1b91f78acd15.png)

引用元：[データ レイクのゾーンとコンテナー - Cloud Adoption Framework | Microsoft Learn](https://learn.microsoft.com/ja-jp/azure/cloud-adoption-framework/scenarios/cloud-scale-analytics/best-practices/data-lake-zones)

本記事では、3つのレイヤーに対応した4つのレイヤー詳細項目別に、ディレクトリを検討します。Raw 以降では、Delta lake 形式のファイルフォーマットで保持することが基本方針です。

| #    | レイヤー  | レイヤー詳細項目 |
| ---- | -------- | -------------- |
| 1    | Bronze   | Landing        |
| 2    | Bronze   | Raw            |
| 3    | Silver   | Enriched       |
| 4    | Gold     | Curated        |


Bronze(Raw) -> Silver(Enriched) にデータを移動する際には、データ型への変換可否等の最小限のデータ品質チェックを実施することが推奨されております。Azure CAFでは、Raw(Bronze)にて、データ品質チェックエラーデータを格納することを想定したディレクトリとなっております。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/363c171d-f51c-d6db-2cca-83bca3d5295b.png)

引用元：[データ品質に関する推奨事項 - Cloud Adoption Framework | Microsoft Learn](https://learn.microsoft.com/ja-jp/azure/cloud-adoption-framework/scenarios/cloud-scale-analytics/govern-data-quality#data-quality-recommendations)

データレイクを実装する際には、データ処理が行う時点（タイムスタンプ）を把握することが重要です。データ挿入日（ingest_date）を厳密に取得することは難しいため、下記表の 3 以降のいずれかの時点でのタイムスタンプを選択します。Datatabricks で取得できる`_metadata.file_modification_time` を利用する場合には、5 の時点となります。

1. ソースシステムにおけるデータ作成時点
2. ソースシステムにおけるデータの最終更新時点
3. ソースシステムからのデータ抽出時点
4. ソースシステムのプログラムのデータインジェスト終了時点
5. データレイクでのオブジェクトの作成時点（最終日更新時点）
6. データエンジニアパイプラインの実行開始時点
7. データレイクにおけるデータに基づいた処理の完了時点

## Azure のクラウド導入フレームワークからのテーラリング方針

### ディレクトリにて利用されている各用語を変更

| #    | 変更前       | 変更後   |
| ---- | ------------ | -------- |
| 1    | Landing      | landing  |
| 2    | Conformance  | raw      |
| 3    | Standardized | enriched |

### キャメルケースとなっている場合には、スネークケースに変更

ドキュメントでは、キャメルケース（例：`Log`）のような記述方法となっているため、スネークケース（例：`log`）に変更します。

### データソースの種類に`Transactional`にて定義されているディレクトリの構成に変更。

ドキュメントでは下記の４つのデータソースの種類が提示されておりその配下のディレクトリンが明示されていないのですが、`Transactional`で提示されている項目をディレクトリ階層にマッピングするように保持させます。

- Log
- Master and Reference
- Telemetry
- Transactional

```text
|--Log
|---{Application Name}
|----{Entity}
```

```text
|--Master and Reference
|---{Source System}
|----{Entity}
```

```text
|--Telemetry
|---{Source System}
|----{Application}
```

```text
|--Transactional
|---{Source System}
|----{Entity}
```

### `Landing` における `Full`にてパーティションを想定

ドキュメントでは`Full`の場合のパーティションが明示されていないのですが、パーティションを設定することを想定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/ee2f91bf-6a66-ec14-5013-7eaf7d964308.png)

引用元：[データ レイクのゾーンとコンテナー - Cloud Adoption Framework | Microsoft Learn](https://learn.microsoft.com/ja-jp/azure/cloud-adoption-framework/scenarios/cloud-scale-analytics/best-practices/data-lake-zones#landing-container-layout)

```text
.
|-Landing
|--Transactional
|---{Source System}
|----{Entity}
|-----{Version}
|------Full
|-------{date (ex. rundate=2019-08-22)}
```

### パーティションを設定する際には、The Small File Problem に気を付けること

パーティションを設定する際には、ファイルサイズが小さくなりすぎないように注意してください。パーティション設定後のファイルサイズが 256 MB 以上とならない場合には、パーティションを設定しないことを検討してください。256 MBのサイズは、下記リンクのDatabricksが自動調整する際のテーブルサイズを参考にしており、目安として捉えてください。

- [テーブルサイズに基づいてファイルサイズを自動調整する - Azure Databricks |マイクロソフト学習 (microsoft.com)](https://learn.microsoft.com/en-us/azure/databricks/delta/tune-file-size#autotune-file-size-based-on-table-size)

### データの連携方法（`Full`や`Delta`）の配下にて パーティションの種類を保持

Spark 等でのデータの読み込み方法を提示することを目的として、パーティション保持方法を記載することとします。基本的には、カラム名と値を保持したHive パーティション形式（例：ingest_date=2020-01-01）を実施するため、`hive`を設定します。`Landing`にて、Hive パーティションを保持できない場合には、`custom`を設定します。 

| #    | 項目      | 説明                                 |
| ---- | --------- | ------------------------------------ |
| 1    | hive   | Hive パーティション形式で保持する場合に指定 |
| 2    | custom | Hive パーティション形式以外で保持する場合に指定       |


### レイヤー詳細項目に`Ingest`と`Development`を追加

Azure CAFには記載されていないがシステム運用上必要となる`Ingest`とAzure CAFには記載されているがメダリオンアーキテクチャにマッピングが難しい`Development`を、レイヤーとして追加します。`Ingest`を、ソースシステムが別ネットワーク上にある場合や Landing のディレクトリ構成でデータ連携できない場合などに一時的なシステムインタフェースレイヤーとして利用します。`Development`は、データコンシューマー（データ利活用者）の要件によりデータを保持するレイヤーであり、必要に応じてメダリオンアーキテクチャに組み込みます。

| #    | レイヤー  | レイヤー詳細項目 |
| ---- | -------- | --------------  |
| 0    | Bronze   | Ingest          |
| 1    | Bronze   | Landing         |
| 2    | Bronze   | Raw             |
| 3    | Silver   | Enriched        |
| 4    | Gold     | Curated         |
| 5    | Other    | Development     |

`Ingest` を利用する場合、かつ、データ挿入日を`5. データレイクでのオブジェクトの作成時点（最終日更新時点）`とする場合には、`Ingest` -> `Landing`のデータ移動時に`Ingest`におけるデータ挿入日の順序を保証する必要があります。


## 各レイヤーでのディレクトリ方針

### 共通の設定値

| #    | 設定値名                     | 設定値例               | 概要                                   |
| ---- | ---------------------------- | ---------------------- | -------------------------------------- |
| 1    | source_system_type           | transactional          | データソースの種別を指定 *1              |
| 2    | source_system                | performance_test       | ソースのシステム名を指定               |
| 3    | entity                       | tpch.sf10.lineitem     | ソースのエンティティ名を指定 *2           |
| 4    | version                      | 0                      | エンティティの管理番号を指定 *3            |
| 5    | loading_pattern              | delta                  | ソースシステムからの連携パターンを指定 *4 |
| 6    | partition_pattern            | hive                   | ソースシステムからのパーティションパターンを指定 *5 |
| 7    | partition_dir                | ingest_date=2022-01-01 | パーティション情報を保持したディレクトリを指定 *6           |
| 8    | data_confidential_class   | general                | データの機密性区分を指定 *7         |

*1 Landing と Raw ではデータソースの特性に応じてディレクトリを構成

データソースの４つの種類が提示されており、それぞれのディレクトリの構成が異なります。データソース種に応じて、`source_system`と`entity`を変更してください。

- log
- Master and Reference
- Telemetry
- Transactional

```text
|--log
|---{application_name}
```

```text
|--master_and_reference
|---{source_system}
```

```text
|--telemetry
|---{source_system}
|----{application}
```

```text
|--transactional
|---{source_system}
```

*2 Entitiy では、データベース名やスキーマ名を `.` （ドット）を置換した単一の単語とした方がいい場合があり

ソースのエンティティが `tpch.sf10.lineitem`という名称である場合には、`.`を、`_`、あるいは、`__`に置換して、`tpch__sf10__lineitem`というようなエンティティ名に変更した方がいいもあります。Spark のテーブルを作成する際に、`.`をテーブル名で利用できなたいため、置換後のディレクトリ名とテーブル名の差異を極力減らすことができます。

*3 管理番号を変更するケース

インジェストされるファイルの形式などが変わる場合に現在の管理番号に加算して管理を実施する。カラムの追加する場合には、Delta lake 形式のテーブルにおけるスキーマ展開機能を利用することで対応ができるため、管理番号の変更は不要です。 Raw(Bronze)以降のディレクトリにてDelta Lake 形式のテーブルを利用する場合には、現時点では 0 のままとなる想定。

*4 データの抽出方法を明示するために、以下のいずれかの項目を設定してください。

| #    | 項目   | 説明                                                      |
| ---- | ------ | --------------------------------------------------------- |
| 1    | full   | 全件データを連携する場合に指定                            |
| 2    | delta  | 差分データを連携する場合に指定           |


*5 以下のいずれかの項目を設定してください。

| #    | 項目      | 説明                                 |
| ---- | --------- | ------------------------------------ |
| 1    | hive   | Hive パーティション形式で保持する場合に指定 |
| 2    | custom | Hive パーティション形式以外で保持する場合に指定       |

`hive` を指定する場合には、`custom`の場合と異なり、`hive`までのパス（例：`/Landing/Transactional/{Source System}/[Entity]/{Version}/delta/hive`）により Spark データフレームを作成することができます。


*6 Hive パーティション形式でディレクトリを構成

パーティションのディレクトリは、カラム名と値を保持したHive パーティション形式（例：ingest_date=2020-01-01）で指定を行い、Delta Lake 形式のテーブルの場合にはパーティション設定を行うことで、テーブルの値から自動でディレクトリが構成されます。パーティションを複数のカラムで実施する場合には、多層ディレクトリとなる場合もあります。

 *7 以下のいずれかの項目を設定してください。

| #    | 項目      | 説明                                 |
| ---- | --------- | ------------------------------------ |
| 1    | general   | 機密以外のデータを保持する場合に指定 |
| 2    | sensitive | 機密データを保持する場合に指定       |

機密データには、DMBOK 2nd(Data Management Body of Knowledge: 2nd Edition)によると、制限付機密と登録者限定機密があるようです。

> 公開用：公衆を含む誰でも利用できる情報
社内向け：情報は従業員やメンバーに限定されるが、共有した場合のリスクは最小限である。
社外秘：適切に結ばれた機密保持契約やそれに類するものがない場合は、組織外で共有することができない情報。
制限付機密：「知る必要がある」一定のロールを持つ個人に限定された情報。
登録者限定機密：非常に機密レベルが高い情報で、データにアクセスするためには、情報にアクセスするすべての人が法的な合意に署名し、秘密のために責任を負わなければならない。

引用元：DMBOK 2nd 第7章データセキュリティ


### 0. Ingest(Bronze) のディレクトリ案
#### 注意事項

`partition_dir`を指定する際にソースシステムのデータに適切なカラムがない場合には、データ連携時の日付を更新日の監査列とする Hive パーティションとすることが有効な場合があります。

#### ディレクトリ案

```text
.
|-ingest
|--{source_system_type}
|---{source_system}
|----[entity]
|-----{version}
|------{loading_pattern}
|-------{partition_pattern}
|--------[partition_dir]
```

### 1. Landing(Bronze) のディレクトリ案
#### 注意事項

`partition_dir`を指定する際にソースシステムのデータに適切なカラムがない場合には、データ連携時の日付を更新日の監査列とする Hive パーティションとすることが有効な場合があります。

#### ディレクトリ案

```text
.
|-landing
|--{source_system_type}
|---{source_system}
|----[entity]
|-----{version}
|------{loading_pattern}
|-------{partition_pattern}
|--------[partition_dir]
```

### 2. Raw(Bronze) のディレクトリ案
#### 注意事項

`partition_dir`を設定する際には、データ挿入日（ingest_date）を設定することが望ましいです。Databricks では、`_metadata.file_modification_time`としてデータ挿入のタイムスタンプを取得できるため、それを日付型として変換して保持することを検討してください。日付型に変換する際には年初や月初にすることで、パーティション後のファイルサイズが適切になる場合もあります。

Azure CAF では ３つのディレクトリ（Delta lake 形式のテーブル）を作成することが想定されていますが、管理が煩雑となる可能性があるため削除フラグ（`delete_flg`）を保持することを前提とした単一テーブルによるディレクトリ案も検討の余地があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/9c8dd28e-71c9-9dbd-4349-b936886c7a41.png)

引用元：[データ レイクのゾーンとコンテナー - Cloud Adoption Framework | Microsoft Learn](https://learn.microsoft.com/ja-jp/azure/cloud-adoption-framework/scenarios/cloud-scale-analytics/best-practices/data-lake-zones#standardized-container)

Azure CAF のにおけるディレクトリ相当のデータを、下記のロジックで抽出できます。エラーデータ等を継続的に抽出する場合には、ビューの作成を検討してください。

- Input  -> `delete_flg`を指定しないで抽出
- Output -> `delete_flg = 0`で抽出
- Error  -> `delete_flg = 1`で抽出

#### ディレクトリ案 1 （単一テーブル）

```tex
.
|-raw
|--{source_system_type}
|---{source_system}
|----[entity]
|-----{version}
|------{loading_pattern}
|-------all
|--------[partition_dir]
```

エラーテーブルを別途作成したい場合には、下記のディレクトリの構築も行ってください。

```tex
.
|-raw
|--{source_system_type}
|---{source_system}
|----[entity]
|-----{version}
|------error
|-------{partition_pattern}
|--------[partition_dir]
```

#### ディレクトリ案 2（複数テーブル）

```text
.
|-raw
|--{source_system_type}
|---{source_system}
|----[entity]
|-----{version}
|------{loading_pattern}
|-------input
|--------[partition_dir]
|-------output
|--------[partition_dir]
|-------error
|--------[partition_dir]
```


### 3. Enriched(Silvler) のディレクトリ案
#### 注意事項

特になし

#### ディレクトリ案

```tex
.
|-enriched
|--{source_system_type}
|---{source_system}
|----[entity]
|-----{version}
|------[partition_dir]
```

### 4. Curated(Gold) のディレクトリ案
#### 注意事項

特になし

#### ディレクトリ案

```text
.
|-curated
|--{curated_group_name}
|---[entity]
|----{version}
|-----{data_confidential_class}
|-------[partition_dir]
```

### 5. Development(Other) のディレクトリ案
#### 注意事項

特になし

#### ディレクトリ案

```text
.
|-development
|--{development_name}
|---[entity]
|----{version}
|-----{data_confidential_class}
|-------[partition_dir] 
```