LoginSignup
8
1

More than 1 year has passed since last update.

マネージドテーブルとアンマネージドテーブルと外部テーブルと

Posted at

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つを意識する必要があります。

使い方の実際

アンマネージドテーブル(外部テーブル)

説明の流れから、アンマネージドテーブル(外部テーブル)から見ていきたいと思います。
ここでのシナリオは、以下のステップを実施して、アンマネージドテーブルの作法を見ていきます。

  1. Spark上でデータフレームとして適当にデータをロードする
  2. 上記のデータをDeltaLake形式でオブジェクトストレージ上に保存する
  3. 上記のデータに対してテーブル名を適当に割り当てて、Metastoreに登録する
  4. 動作確認
  5. データを削除する

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)

image.png

2. 上記のデータをDeltaLake形式でオブジェクトストレージ上に保存する

(
  df.write.format('delta')
  .mode('overwrite')
  .save('/tmp/diamond.delta')
)

念の為、ストレージ上にファイル保存されたことを確認しておく。

%fs ls /tmp/diamond.delta

image.png

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

image.png

また、テーブルのメタデータも確認してみます。

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]|       |
+----------------------------+---------------------------------------------------+-------+

上記の結果より、メタデータとして、ファイルパスの他に、スキーマやテーブルプロパティなどが含まれていることがわかります。また、TypeEXTERNALとなっていて、「外部テーブル」(=「アンマネージドテーブル」)であることもわかります。

5. データを削除する

最後にデータの削除をしていきます。先ほど説明した通り、テーブルメタデータを削除した後で、ファイル実体も削除します。

まずは、メタデータの削除。

%sql
DROP TABLE diamond_delta;

続いて、ファイルの削除

%fs rm -r /tmp/diamond.delta

これでアンマネージドテーブルの削除が完了しました。

アンマネージドテーブルの特徴

いかがでしょうか。アンマネージドテーブルの場合は、要はファイルとテーブル名が分離していると言うことです。これにより、運用上のいくつかのメリット・デメリットが見えてくると思います。

良い点としては、

  • 保存するファイルのストレージロケーション(バケツ、コンテナ、フォルダ構成、ファイルパス)などがユーザー側で制御できる
  • ファイルのバックアップ運用などと柔軟に調和できる
  • 一つのデータを複数のメタストアに登録して、複数の環境から参照できる

などがあると思います。

一方で、面倒だと思う点として、

  • テーブルを削除するのに2段階踏まないといけない
  • 使う側としては、テーブル名だけを指定して、ストレージ保存先は適当に決めてほしい
  • 使う側に対して、ストレージ情報は隠蔽・抽象化させておきたい(ストレージ情報を公開、提供しないといけない)

などがあると思います。

こうした場合は、マネージドテーブルが便利になります。

マネージドテーブル

それではマネージドテーブルの作法を見ていきます。

  1. Spark上でデータフレームとして適当にデータをロードする
  2. テーブル名を使って保存(永続化)する
  3. 動作確認
  4. データを削除する

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

image.png

メタデータの確認をしてみみます。

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が自動的にアサインされたファイルの保存先
  • TypeMANAGEDになっている

というのが先ほどとの違いとしてわかります。

4. データを削除する

マネージドテーブルの場合は、DROP TABLEするだけで、関連するファイルも同時に削除されます。

%sql
DROP TABLE diamond_delta_managed

マネージドテーブルの特徴

先ほどのアンマネージドテーブルの逆になりますが、ユーザーはテーブル名だけを管理すればよく、ストレージ側は意識する必要がなくなる、と言う点がポイントかと思います。

まとめ

今回は、マネージドテーブルとアンマネージドテーブル(外部テーブル)を説明しました。特に要件がない場合は、マネージドテーブルを使うのがシンプルになると思います。実際の使用する環境や場面に応じて使い分けてください。

8
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
1