Deltaクローンの襲来(ディザスターリカバリーの可用性、複雑性に立ち向かう)

[Simplifying Big Data Disaster Recovery with Delta Clones \- The Databricks Blog](https://databricks.com/blog/2021/04/20/attack-of-the-delta-clones-against-disaster-recovery-availability-complexity.html)の翻訳です。

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

ノートブック：[Using Deep Clone for Disaster Recovery with Delta Lake on Databricks](https://databricks.com/wp-content/uploads/notebooks/using-deep-clone-disaster-recovery-delta-lake-databricks.html)

多くのビジネスにとって、災害イベント、緊急事態や他の問題においてデータストアのような重要なサービスがオンラインであり続けるようにするために、[ビジネス継続性プラン](https://en.wikipedia.org/wiki/Business_continuity_planning)の作成は非常に重要となっています。多くの場合、ハリケーンのような災害や、未知のイベントによって引き起こされる稀なリージョナルのクラウド障害においても、データチームがDatabricksプラットフォームを使いつづけられることはミッションクリティカルなこととなっています。[Azure](https://docs.microsoft.com/en-us/azure/databricks/administration-guide/disaster-recovery)や[AWS](https://qiita.com/taka_yayoi/items/8ce4f36a155317318fa1)のディザスターリカバリーガイドにあるように、Databricksは多くの場合、上流のデータ取り込み、洗練されたデータパイプライン、クラウドネイティブのストレージ、機械学習と人工知能、ビジネスインテリジェンスと[オーケストレーション](https://databricks.com/jp/glossary/orchestration)を含むデータエコシステム全体のコアパーツとなっています。いくつかのユースケースは特に、リージョン規模の障害にセンシティブかもしれません。

**本書で説明するでィザスターリカバリーの全体の流れを説明する動画(別のブラウザタブが開きます)**

https://player.vimeo.com/video/536399768

みなさまのデータインフラストラクチャの継続性を保証し、復旧のためのツール、ポリシー、手順を策定する[ディザスターリカバリー](https://en.wikipedia.org/wiki/Disaster_recovery)は、あらゆるビジネス継続性プランにおいて重要なコンポーネントとなっています。[Deltaクローン](https://databricks.com/blog/2020/09/15/easily-clone-your-delta-lake-for-testing-sharing-and-ml-reproducibility.html)は、データ複製をシンプルなものとし、お使いのDeltaテーブルに対する効果的なリカバリー戦略の立案を可能にします。Deltaクローンを用いることで、あなたのプライマリーサイトとセカンダリーサイト、あるいはリージョン間で適切な順序でデータを容易かつインクリメンタルに同期することができるようになります。DeltaはRDBMSレプリケーションがデータベースを安定状態に復旧する際にログを使用するのと同じように、この同期を行うためにトランザクションログを使用します。クラウドのマルチリージョンの同期のようなソリューションが問題の一部を解決するかもしれませんが、これらの処理は通常非同期であり、オペレーションがうまく動作せずデータの破損を引き起こすことになります。

本書では、Deltaクローンがどのようにこれらの問題を回避し、データセンター間のデータ同期のプロセスをコントロールすることでDRを押し進めるのかを説明します。

# クローンとは？

おそらくですが、最初の質問は「クローンって何？」ではないでしょうか。

クローンとは、ある時点におけるソーステーブルの複製です。これらは、ソーステーブルと同じメタデータを持ちます。すなわち、同じスキーマ、同じ制約、同じコラムのコメント、統計情報、パーティショニングを持ちます。しかし、クローンはソーステーブルとは別の独立した履歴を持つことに注意してください。例えば、ソーステーブルとクローンに対するタイムトラベルのクエリーは同じ結果を返さない場合があります。

シャロー(浅い、あるいはゼロコピー)クローンは、クローンされるテーブルのメタデータのみを複製します。テーブル自身のデータファイルはコピーされません。このタイプのクローンは別の物理的なデータコピーを作成しないので、ストレージコストは最小になります。シャロークローンはリソース集約型ではないので、非常に高速に作成することができます。しかし、これらのクローンは自己完結型ではなく、クローンしたソースに対する依存関係を維持します。シャロークローンは、実際にプロダクションテーブルを変更することなしに、プロダクションテーブルに対する構成変更をステージングするようなテストや実験にはメリットがあります。詳細に関しては、[Easily Clone your Delta Lake for Testing, Sharing, and ML Reproducibility](https://databricks.com/blog/2020/09/15/easily-clone-your-delta-lake-for-testing-sharing-and-ml-reproducibility.html)をご覧ください。

ディープクローンは、クローンされるテーブルのメタデータとデータファイルの完全なコピーを作成します。これはCTAS(CREATE TABLE... AS... SELECT...)コマンドと同じようなものです。しかし、ある時点でのオリジナルテーブルの現行バージョンの公正なコピーを作成するので、CTASでは指定しなくてはならないパーティショニングのオプション、制約や他の情報を指定する必要はありません。さらに、はるかに高速で堅牢であり、インクリメンタルに動作することも可能です。最後に、全てのデータではなく、障害から防御すべきデータのみを複製する効率的なソリューションを実現できる点が重要です。
![](https://databricks.com/wp-content/uploads/2021/04/delta-clone-dr-blog-img-1-1024x477.jpg)
*ソース: https://raw.githubusercontent.com/databricks/tech-talks/master/images/delta-lake-deep-clone.png*

ディープクローンは以下のケースで有用です。

- プロダクションのデータプロセスのリスクを与えず、ユーザーに影響を与えることなしに、プロダクション環境におけるテストの実施
- プロダクションテーブルに対する大きな変更のステージング
- ML結果の再現性の保証
- データ移行、データ共有やアーカイブ

本書では、ディザスターリカバリーにおけるDeltaのディープクローンの役割にフォーカスします。

# クローンを見せて！

以下のSQLコマンドでクローンを作成することができます。

```sql:SQL
CREATE OR REPLACE TABLE loan_details_delta_clone
DEEP CLONE loan_details_delta;
```

以下のSQL文を用いることでオリジナルのテーブル(`loan_details_delta`)とクローンされたテーブル(`loan_details_delta_clone`)両方に対してクエリーを発行することができます。

```sql:SQL
-- Original view of data
SELECT addr_state, funded_amnt FROM loan_details_delta GROUP BY addr_state, funded_amnt

-- Clone view of data
SELECT addr_state, funded_amnt FROM loan_details_delta_clone GROUP BY addr_state, funded_amnt
```

以下の図ではDatabricksノートブックの地図の可視化を用いた結果を示しています。
![](https://databricks.com/wp-content/uploads/2021/04/delta-clone-dr-blog-img-2-1024x437.jpg)
*https://raw.githubusercontent.com/databricks/tech-talks/master/images/delta-lake-original-clone-view-map.png*

ディープクローンの重要な機能には、インクリメンタルなアップデートがあります。オリジナルとクローンの間の一貫性を保証するためにテーブル全体をコピーするのではなく、データに加えられた変更行(更新、削除、マージ、インサートされたレコード)のみが、次回テーブルがクローンされる時にコピー、更新されます。
![](https://databricks.com/wp-content/uploads/2021/04/delta-clone-dr-blog-img-3-1024x307.jpg)
*https://raw.githubusercontent.com/databricks/tech-talks/master/images/deep-clone-incremental-update.png*

ディープクローンでインクリメンタルなアップデートがどのように動作するのかを説明するために、以下のSQLを用いてオリジナルテーブルから行を削除してみましょう(上の図におけるステップ1)。

```sql:SQL
DELETE FROM loan_details_delta WHERE addr_state = 'OH';
```

この時点では、オリジナルテーブル(`loan_details_delta`)にはオハイオ(OH)の行は含まれていませんが、クローンされたテーブル(`loan_details_delta_clone`)にはこれらの行は残っています。二つのテーブルを再度同期するには、再びクローンのオペレーションを実行します(ステップ2)。

```sql:SQL
CREATE OR REPLACE TABLE loan_details_delta_clone
DEEP CLONE loan_details_delta
```

クローンテーブルとオリジナルテーブルは同期状態に戻ります(これは以降のセクションでさらに明らかになります)。しかし、オリジナルテーブルのコンテンツ全体をコピーするのではなく、インクリメンタルに行が削除されるので、劇的にプロセスを高速にします。クローンされたテーブルはオリジナルテーブルで更新された行のみを更新するので、この場合、オハイオに該当する行が削除されますが、他の行は変更されません。

# OK、このブログの障害から復旧しましょう

クローンによるディザスターリカバリーはコンセプト的にはわかりやすいものですが、あらゆるDBAやDevOpsエンジニアが証言するように、ディザスターリカバリーのソリューションの実際の実装はさらに複雑なものとなります。多くのプロダクションシステムは、RDBMSにおけるアクティブ-アクティブクラスターと同じような双方向のディザスターリカバリープロセスを必要とします。アクティブなサーバー(*ソース*)がオフラインになった際、セカンダリーサーバー(*クローン*)は読み書きのオペレーションを行えるようにオンラインになる必要があります。以降のシステム対する変更(データに対するインサート、変更)は、今時点でクラスターでアクティブであるセカンダリーサーバーに対して行われます。そして、オリジナルのサーバー(*ソース*)がオンラインに復帰したら、*クローン*に対して行われた全ての変更を*ソース*に再度同期する必要があります。再同期の完了を持って、*ソース*サーバーが再度アクティブに復帰し、*クローン*サーバーはセカンダリーの状態に戻ります。システムは継続的にデータを提供し、データを変更し続けるため、データの損失をなくすためにデータのコピーはクイックに同期することが重要となります。

ディープクローンを用いることで、以下の図に示すようにマルチリージョンの分散システムでおいても、このワークフローを容易に実行することができます。
![](https://databricks.com/wp-content/uploads/2021/04/delta-clone-dr-blog-img-4-1-1024x343.jpg)
*ソース: https://raw.githubusercontent.com/databricks/tech-talks/master/images/delta-lake-deep-clone-timeline.png*

この例では、*ソース*はアクティブなDatabrikcsのリージョンのテーブルであり、*クローン*はセカンダリーリージョンのテーブルとなります。

- t0時点: insert/update文は*ソース*テーブルに対して実行され、*ソース*と*クローン*テーブルが同期状態にあり続けるように`DEEP CLONE`を実行します。
- t1時点: 2つのテーブルは同期しています。
- t2時点: *ソース*テーブルにアクセスできません。*クローン*テーブルが、このタイムスタンプ以降の全てのクエリーとデータ変更が行われる*ソース'* になります。
- t3時点: DELETE文が*ソース'* に実行されます。
- t4時点: *ソース*テーブルにアクセスできるようになりましたが、*ソース'* と*ソース*は同期していません。
- t4'時点: *ソース'* と*ソース*を同期するために`DEEP CLONE`を実行します。
- t5時点: 2つのコピーは同期されたので、*ソース*は*アクティブ*テーブルとして稼働を再開し、*ソース'* は*セカンダリー*テーブル、*クローン*に戻ります。

次に、DatabricksでSQLコマンドを用いてこれらのステップをどのように行うのかを説明します。[Using Deep Clone for Disaster Recovery with Delta Lake on Databricks](https://github.com/databricks/tech-talks/raw/master/samples/Using%20Deep%20Clone%20for%20Disaster%20Recovery%20with%20Delta%20Lake%20on%20Databricks.dbc)のDatabricksノートブックを実行することで、作業を再現することもできます。

# ソースの変更

通常のデータ処理の一部として、アクティブリージョン(*ソース*)のテーブルのデータを変更するところからスタートします。

![](https://databricks.com/wp-content/uploads/2021/04/delta-clone-dr-blog-img-5-1024x368.jpg)
*ソース: https://raw.githubusercontent.com/databricks/tech-talks/master/images/delta-lake-deep-clone-timeline-0.png*

![](https://databricks.com/wp-content/uploads/2021/04/delta-clone-dr-blog-img-6-1024x507.jpg)
*ソース: https://raw.githubusercontent.com/databricks/tech-talks/master/images/delta-lake-insert-update-example.png*

この場合、`TX`に対する`UPDATE`文(`Amount`カラムの更新)と`OH`に対する`INSERT`文(`TX`と`AZ`のエントリーに基づく新規行の挿入)を用いてマージを実装します。

ソーステーブルとクローンテーブルのバージョンチェックします。このシナリオはloan_details_deltaとloan_details_delta_cloneテーブルが同期しているt0時点からスタートしています。そして、loan_details_deltaテーブルを変更しました。どうすれば、両方のテーブルをクエリーして比較することなしに、データのバージョンが同じと言えるのでしょうか？Delta Lakeでは、この情報はトランザクションログに記録されているので、`DEEP CLONE`文は一つのテーブルクエリーでソースとクローンのバージョンの両方を自動で特定することができます。

`DESCRIBE HISTORY DeltaTable`を実行した際には、以下のようなスクリーンショットのような結果を確認できるはずです。
![](https://databricks.com/wp-content/uploads/2021/04/delta-clone-dr-blog-img-7-1024x230.jpg)

:::note info
**注意**
オリジナルのテーブルは`loan_details_delta`であり、クローンテーブルは`loan_details_delta_clone`です。
:::

これを詳細に見ていきます。

- ソーステーブルにおいては、テーブルバージョンを特定する最も最新のバージョン番号はここではバージョン2となります。
- クローンテーブルにおいては、クローンテーブルがどのソースバージョンから作られたのかを特定するために、最新の`operationParameters.sourceVersion`をクエリーします。ここではバージョン1となります。

上で述べたように、この情報は全てDelta Lakeのトランザクションログに格納されています。また、二つのテーブルのバージョンを確認するためにトランザクションログに対してクエリーを行うために関連ノートブックに含まれている`checkTableVersions()`関数を使うこともできます。

```py:Python
checkTableVersions()

Delta Lake Original Table Version: 2, Cloned Table Version: 1
```

ログの詳細については、[Delta Lakeにダイビング：トランザクションログを読み解く](https://qiita.com/taka_yayoi/items/b7f628c219463e055592)をご覧ください。

# ソースとクローンの再同期

上のセクションで見てきたように、今ではソーステーブルはv2ですが、クローンテーブルはv1となっています。
![](https://databricks.com/wp-content/uploads/2021/04/delta-clone-dr-blog-img-8.png)
*ソース: https://raw.githubusercontent.com/databricks/tech-talks/master/images/delta-lake-deep-clone-timeline-0b1.png*

二つのテーブルを同期するために、t1時点で以下のコマンドを実行します。

```sql:SQL
CREATE OR REPLACE TABLE loan_details_delta_clone
DEEP CLONE loan_details_delta;
```

![](https://databricks.com/wp-content/uploads/2021/04/delta-clone-dr-blog-img-9.png)
*ソース: https://raw.githubusercontent.com/databricks/tech-talks/master/images/delta-lake-deep-clone-timeline-1.png*

# ソーステーブルにアクセスできません

t2時点で、*ソース*テーブルにアクセスできなくなりました。ユーザーの誤操作あるいはリージョンのダウンなど、この原因がなんであれ、これが目を覚ますための偉大なライフハックではないことを認めなくてはなりません。

![](https://databricks.com/wp-content/uploads/2021/04/delta-clone-dr-blog-img-twitter.jpg)
*ソース: https://www.reddit.com/r/ProgrammerHumor/comments/kvwj9f/burn_the_backups_if_you_need_that_extra_kick*

*私がどのようにモーニングルーチンをハックしたのか: プロダクションデータベースを消去したら100%完全に目が覚めました。*

冗談はさておき、実際のところ、あなたはプロダクションシステムがオフラインになることに常に備えていなくてはなりません。幸運なことに、あなたの[ビジネス継続性プラン](https://en.wikipedia.org/wiki/Business_continuity_planning)のおかげで、データを読み書きするためにお使いのサービスをクローンにリダイレクトできるセカンダリーのクローンがあります。

# データの訂正

オリジナルの*ソース*が利用できないため、セカンダリーリージョン(*クローン*)が今では*ソース'* テーブルになります。
![](https://databricks.com/wp-content/uploads/2021/04/delta-clone-dr-blog-img-9.jpg)
*ソース: https://raw.githubusercontent.com/databricks/tech-talks/master/images/delta-lake-deep-clone-timeline-2.png*

すぐにデータを変更する必要がないいくつかのサービスは*ソース*にアクセスできない間、read-onlyモードに切り替えることができますが、多くのサービスやプロダクション環境はそのような遅延を許容できないでしょう。この場合、t3時点では*ソース'* データを変更し、いくつかのレコードをDELETEしなくてはなりません。

```sql:SQL
-- Running `DELETE` on the Delta Lake Source' table
DELETE FROM loan_detail_delta_clone WHERE addr_state = 'OH';
```

テーブル履歴を確認するか、`checkTableVersions()`を実行すると、(*Source’* )テーブルはDELETE文の実行後にバージョン4になっています。

```py:Python
# Check the table versions
checkTableVersions(2)

Delta Lake Original Table Version: None, Cloned Table Version: 4
```

オリジナルの*ソース*テーブルには到達できないので、バージョンは*None*となります。
![](https://databricks.com/wp-content/uploads/2021/04/image13-1024x365.png)
*ソース: https://raw.githubusercontent.com/databricks/tech-talks/master/images/delta-lake-deep-clone-timeline-3b.png*

クローンテーブルがバージョン4である理由は、履歴を確認することでクイックに特定することができ、以前に実行した3つの`CLONE`オペレーションと`DELETE`コマンドです。

```sql:SQL
DESCRIBE HISTORY loan_details_delta_clone
```

![Screen Shot 2022-06-01 at 17.45.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/e561424d-8a65-cde7-b837-a6da263f5e80.png)

# ソースに戻す

やりました、`<何かしらの修正>`のあとで、オリジナルの*ソース*テーブルがオンラインに戻りました！
![](https://databricks.com/wp-content/uploads/2021/04/delta-clone-dr-blog-img-13.png)
*ソース: https://raw.githubusercontent.com/databricks/tech-talks/master/images/delta-lake-deep-clone-timeline-4b.png*

しかし、以前のステップで見たように、*ソース*と*ソース'* レプリカの間には違いがあります。幸運なことにこの問題の修正は簡単です。

```sql:SQL
/* Fail back from the `loan_details_delta_clone` table to the `loan_details_delta` table */
CREATE OR REPLACE TABLE loan_details_delta
DEEP CLONE loan_details_delta_clone;
```

オリジナルの*ソース*テーブル(`loans_details_delta_clone`)を置き換えるために`DEEP CLONE`を実行することで、クイックにオリジナルの状態に戻すことができます。

![](https://databricks.com/wp-content/uploads/2021/04/delta-clone-dr-blog-img-14-1024x336.jpg)
*ソース: https://raw.githubusercontent.com/databricks/tech-talks/master/images/delta-lake-deep-clone-timeline-5.png*

これで、我々の全てのサービスがオリジナルの*ソース*、*ソース'* テーブルが*クローン*(セカンダリー)の状態にまで戻りました。

何人かの人は、今では*クローン*テーブルのバージョンは4ですが、*ソース*テーブルのバージョンが3であることに気づいたかもしれません。これは、バージョン番号はテーブルに対してなされたオペレーションの数に関連づけられることに起因します。この場合、*ソース*テーブルになされたオペレーションの数は少ないです。

```sql:SQL
DESCRIBE HISTORY loan_details_delta
```

```
| ------- | --------- | --- | --------- | --- |
| version | timestamp | --- | operation | --- |
| ------- | --------- | --- | --------- | --- |
| 3       | ...       | ... | CLONE     | ... |
| 2       | ...       | ... | MERGE     | ... |
| 1       | ...       | ... | DELETE    | ... |
| 0       | ...       | ... | WRITE     | ... |
```

# 注意事項

ディザスターリカバリーに対するこの手法は、障害に関係なく読み書きの両方の可用性を保証します。しかし、途中の変更を失う可能性があるというコストが存在します。以下のシナリオを考えてみます。
![](https://databricks.com/wp-content/uploads/2021/04/delta-clone-dr-blog-img-4.jpg)

2つの`CLONE`オペレーションの間のt=1でアップデートがあることに注意します。2回目の`CLONE`オペレーションが行われるとt=4の時点でこの間隔で行われた変化が失われる可能性があります。いかなる変更も失われないようにするためには、t=1とt=4の間でソーステーブルに書き込みを行わないことを保証する必要があります。リージョン全体が不調になる可能性があることを考えると、これを達成することは難しいです。とはいえ、可用性が最も重要な検討事項であるユースケースは多く存在します。

# まとめ

本書では、DatabricksのDelta Lakeの`DEEP CLONE`機能を用いた双方向ディザスターリカバリーの実行方法をデモンストレーションしました。Delta LakeのSQL文のみを用いることで、ビジネス継続性プランの一部として、データのレプリケーションを劇的にシンプルにし、高速化することができます。Deltaクローンの詳細に関しては、[Easily Clone your Delta Lake for Testing, Sharing, and ML Reproducibility](https://databricks.com/blog/2020/09/15/easily-clone-your-delta-lake-for-testing-sharing-and-ml-reproducibility.html)を参照ください。ご自身でDatabricksランタイムを用いてこのエクササイズをウォークスルーするために[Using Deep Clone for Disaster Recovery with Delta Lake on Databricks](https://github.com/databricks/tech-talks/raw/master/samples/Using%20Deep%20Clone%20for%20Disaster%20Recovery%20with%20Delta%20Lake%20on%20Databricks.dbc)をチェックしてみてください。

# 謝辞

この記事に貢献してくれたPeter Stern, Rachel Head, Ryan Kennedy, Afsana Afzal, Ashley Trainorに感謝の意を表します。

### Databricks 無料トライアル

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