Apache Sparkでやや混同しがちなテーブル種類について説明します。要約を触れた後に、詳細を説明します。
ざっくり言うと
マネージドテーブル は、ユーザーがテーブル名だけを使ってデータ(Sparkデータフレーム)を永続化する方法で、データの保存先(ストレージとパス)はSparkシステム側で自動的にアサインされます。つまり、ユーザーは保存先などストレージ側を意識する必要がありません。データを削除する場合も、DROP TABLE
文を実行するだけで済み、テーブルのメタデータの削除と、データ実体の削除が同時に実行されます。データの保存先が自動管理されるので 「マネージド」テーブル と呼びます。
一方、アンマネージドテーブルは、ユーザーがデータの保存先パスを指定して永続化し、後からテーブル名とデータ保存先をリンクします。この場合、データの削除には、DROP TABLE
文でのテーブル(のメタデータ)削除に加えて、データ保存先のデータ実体を削除する必要があります。つまり、ユーザーはテーブル(メタデータ)とデータ実体の保存先の両方を意識する必要があり、よって、「アンマネージド」テーブルと呼ばれています。この方法だと、Sparkシステム管理のストレージ外上に置かれているデータに対してもテーブル名とリンクができるため、この意味で、アンマネージドテーブルは「外部テーブル(External Table)」 とも呼ばれます。
上記の説明は、いくつかの前提知識がないと理解できないと思いますので、それを含めて以下詳細を説明します。
詳細
Apache Sparkにおけるデータの実体とテーブル・メタデータ
Apache Sparkをはじめとするデータレイク系のフレームワークでは、ストレージ上にCSV, JSON, Parquet, DeltaLake, Avroなどのファイルとして保存しておいて、それらをプログラム上でテーブル形式にして扱うことが一般的です。例えば、SparkのPython APIであるPySparkだと、df = spark.read.csv('/path/to/file.csv')
のようにデータフレームとして読み込ませることができ、CSVファイルをそのままテーブル形式として扱うことができます。つまり、データを「ファイル」として扱うことが基本的になります。
一方で、テーブルを扱うなら、特に検索や集計処理をする場合であれば、SQLを使いたくなるのは自然な流れだと思います。ただし、SQLは「ファイル」ではなく「テーブル名」としてデータ参照を行う必要があります。
このギャップを埋めるために、Apache SparkではMetastoreと呼ばれるサービス上で「テーブル名」と「ファイル」の紐付けができる様になっています。このMetastoreサービスのオプションとしては、ドライバノードローカル(Derby), Hive Metastore, Unity Catalog(Databricksプロダクト)などがあります(Sparkのシステムの環境設定で定義されています)。もちろん、SQLのテーブルとして使用するため、テーブル名に対応するファイル保存先パスの他にも、カラム名などのスキーマ情報や、テーブル設定(テーブルプロパティ)なども同時に記録管理されます。
以上、まとめると、Apache Sparkではデータ保存(永続化)とその参照の枠組みとして、データの実態はファイルとしてストレージ上に保存し、そのデータに紐づくテーブル名やスキーマ情報などはMetastoreに保存されます。つまり、ユーザーは、データの実体とテーブル・メタデータの2つを意識する必要があります。
使い方の実際
アンマネージドテーブル(外部テーブル)
説明の流れから、アンマネージドテーブル(外部テーブル)から見ていきたいと思います。
ここでのシナリオは、以下のステップを実施して、アンマネージドテーブルの作法を見ていきます。
- Spark上でデータフレームとして適当にデータをロードする
- 上記のデータをDeltaLake形式でオブジェクトストレージ上に保存する
- 上記のデータに対してテーブル名を適当に割り当てて、Metastoreに登録する
- 動作確認
- データを削除する
1. Spark上でデータフレームとして適当にデータをロードする
df = (
spark.read
.format('csv')
.option('Header', True)
.option('inferSchema', True)
.load('/databricks-datasets/Rdatasets/data-001/csv/ggplot2/diamonds.csv')
)
display(df)
2. 上記のデータをDeltaLake形式でオブジェクトストレージ上に保存する
(
df.write.format('delta')
.mode('overwrite')
.save('/tmp/diamond.delta')
)
念の為、ストレージ上にファイル保存されたことを確認しておく。
%fs ls /tmp/diamond.delta
3. 上記のデータに対してテーブル名を適当に割り当てて、Metastoreに登録する
上記で保存したDeltaLakeのファイルに対して、テーブル名を割り当て、紐付けます。ここでは、テーブル名としてdiamond_delta
を使います。
%sql
CREATE TABLE diamond_delta -- テーブル名
USING delta -- ファイルのフォーマット
LOCATION '/tmp/diamond.delta'; -- ファイルのパス
これで、データ実体のファイル/tmp/diamond.delta
とテーブル名diamond_delta
が紐付けられました。
4. 動作確認
それでは、SQLで上記のデータを参照してみましょう。
%sql
SELECT * FROM diamond_delta
また、テーブルのメタデータも確認してみます。
spark.sql('DESCRIBE EXTENDED diamond_delta').show(n=30, truncate=False)
-----
[結果をテキストで見るために、`spark.sql().show()`を使いました]
+----------------------------+---------------------------------------------------+-------+
|col_name |data_type |comment|
+----------------------------+---------------------------------------------------+-------+
|_c0 |int |null |
|carat |double |null |
|cut |string |null |
|color |string |null |
|clarity |string |null |
|depth |double |null |
|table |double |null |
|price |int |null |
|x |double |null |
|y |double |null |
|z |double |null |
| | | |
|# Detailed Table Information| | |
|Catalog |hive_metastore | |
|Database |default | |
|Table |diamond_delta | |
|Type |EXTERNAL | |
|Location |dbfs:/tmp/diamond.delta | |
|Provider |delta | |
|Owner |root | |
|Table Properties |[delta.minReaderVersion=1,delta.minWriterVersion=2]| |
+----------------------------+---------------------------------------------------+-------+
上記の結果より、メタデータとして、ファイルパスの他に、スキーマやテーブルプロパティなどが含まれていることがわかります。また、Type
がEXTERNAL
となっていて、「外部テーブル」(=「アンマネージドテーブル」)であることもわかります。
5. データを削除する
最後にデータの削除をしていきます。先ほど説明した通り、テーブルメタデータを削除した後で、ファイル実体も削除します。
まずは、メタデータの削除。
%sql
DROP TABLE diamond_delta;
続いて、ファイルの削除
%fs rm -r /tmp/diamond.delta
これでアンマネージドテーブルの削除が完了しました。
アンマネージドテーブルの特徴
いかがでしょうか。アンマネージドテーブルの場合は、要はファイルとテーブル名が分離していると言うことです。これにより、運用上のいくつかのメリット・デメリットが見えてくると思います。
良い点としては、
- 保存するファイルのストレージロケーション(バケツ、コンテナ、フォルダ構成、ファイルパス)などがユーザー側で制御できる
- ファイルのバックアップ運用などと柔軟に調和できる
- 一つのデータを複数のメタストアに登録して、複数の環境から参照できる
などがあると思います。
一方で、面倒だと思う点として、
- テーブルを削除するのに2段階踏まないといけない
- 使う側としては、テーブル名だけを指定して、ストレージ保存先は適当に決めてほしい
- 使う側に対して、ストレージ情報は隠蔽・抽象化させておきたい(ストレージ情報を公開、提供しないといけない)
などがあると思います。
こうした場合は、マネージドテーブルが便利になります。
マネージドテーブル
それではマネージドテーブルの作法を見ていきます。
- Spark上でデータフレームとして適当にデータをロードする
- テーブル名を使って保存(永続化)する
- 動作確認
- データを削除する
1. Spark上でデータフレームとして適当にデータをロードする
こちらは先ほどと同じです(再掲)。
df = (
spark.read
.format('csv')
.option('Header', True)
.option('inferSchema', True)
.load('/databricks-datasets/Rdatasets/data-001/csv/ggplot2/diamonds.csv')
)
2. テーブル名を使って保存(永続化)する
saveAsTable()
にテーブル名を渡すだけです。ここでは、diamond_delta_managed
というテーブル名を使いました。
(
df.write.format('delta')
.mode('overwrite')
.saveAsTable('diamond_delta_managed')
)
上記を実行すると、ファイルの保存先(ストレージ)はシステム側で自動的に払い出してくれ、上記のテーブル名との紐付けも行ってくれます。楽です。
3. 動作確認
動作確認方法は、先ほどと同様です。
まずテーブルの参照です。
%sql
SELECT * FROM diamond_delta_managed
メタデータの確認をしてみみます。
spark.sql('DESCRIBE EXTENDED diamond_delta_managed').show(n=30, truncate=False)
-----
[結果をテキストで見るために、`spark.sql().show()`を使いました]
+----------------------------+---------------------------------------------------+-------+
|col_name |data_type |comment|
+----------------------------+---------------------------------------------------+-------+
|_c0 |int |null |
|carat |double |null |
|cut |string |null |
|color |string |null |
|clarity |string |null |
|depth |double |null |
|table |double |null |
|price |int |null |
|x |double |null |
|y |double |null |
|z |double |null |
| | | |
|# Detailed Table Information| | |
|Catalog |hive_metastore | |
|Database |default | |
|Table |diamond_delta_managed | |
|Type |MANAGED | |
|Location |dbfs:/user/hive/warehouse/diamond_delta_managed | |
|Provider |delta | |
|Owner |root | |
|Is_managed_location |true | |
|Table Properties |[delta.minReaderVersion=1,delta.minWriterVersion=2]| |
+----------------------------+---------------------------------------------------+-------+
結果から分かることとして、
-
Location
が自動的にアサインされたファイルの保存先 -
Type
がMANAGED
になっている
というのが先ほどとの違いとしてわかります。
4. データを削除する
マネージドテーブルの場合は、DROP TABLE
するだけで、関連するファイルも同時に削除されます。
%sql
DROP TABLE diamond_delta_managed
マネージドテーブルの特徴
先ほどのアンマネージドテーブルの逆になりますが、ユーザーはテーブル名だけを管理すればよく、ストレージ側は意識する必要がなくなる、と言う点がポイントかと思います。
まとめ
今回は、マネージドテーブルとアンマネージドテーブル(外部テーブル)を説明しました。特に要件がない場合は、マネージドテーブルを使うのがシンプルになると思います。実際の使用する環境や場面に応じて使い分けてください。