Databricksにおけるファイルの操作方法

[How to work with files on Databricks \| Databricks on AWS](https://docs.databricks.com/files/index.html)　[2023/1/25時点]の翻訳です。

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

[DBFS](https://qiita.com/taka_yayoi/items/f15b50469b368907ec3b)、クラスターのローカルドライバーノード、クラウドオブジェクトストレージ、外部ロケーション、[Databricks Repos](https://docs.databricks.com/repos/work-with-notebooks-other-files.html#create-and-edit-files-and-directories-programmatically)のファイルを操作することができます。他のシステムをインテグレーションすることができますが、これらの多くはDatabricksに対して直接のファイルアクセスを提供しません。

本書では、稼働中のクラスターにアタッチされる揮発性のボリュームストレージに格納されるファイル、DBFSルートに格納されるファイルを操作する際の違いを理解することにフォーカスします。ここで説明するDBFSルートのコンセプトはマウントされたオブジェクトストレージにも直接適用することができます。これは、`/mnt`ディレクトリはDBFSルートの配下に存在するからです。また、サンプルの多くは、必要な権限を持っていればクラウドオブジェクトストレージや外部ロケーションの直接の操作にも適用することができます。

# Databricksのルートパスとは何か？

Databricksにおけるルートパスは、実行されるコードによって異なります。

DBFSルートはSparkとDBFSコマンドのルートパスとなります。これには以下が含まれます。

- Spark SQL
- Sparkデータフレーム
- `dbutils.fs`
- `%fs`

ドライバーにアタッチされるブロックストレージボリュームは、ローカルで実行されるコードのルートパスとなります。これには以下が含まれます。

- `%sh`
- 大部分のPythonコード(PySparkを除く)
- 大部分のScalaコード(Sparkを除く)

:::note
**注意**
Databricks Reposを使っている場合、`%sh`のルートパスは現在お使いのレポのディレクトリとなります。詳細は[プログラムでDatabricksワークスペースのファイルを操作する](https://qiita.com/taka_yayoi/items/14c74e07f41d4e93ee45)をご覧ください。
:::

# DBFSルートのファイルにアクセスする

DBFSルートがデフォルトとなっているコマンドを使用する際、相対パスあるいは`dbfs:/`を含めることができます。

```sql:SQL
SELECT * FROM parquet.`<path>`;
SELECT * FROM parquet.`dbfs:/<path>`
```

```py:Python
df = spark.read.load("<path>")
df.write.save("<path>")
```

```py:Python
dbutils.fs.<command> ("<path>")
```

```bash:Bash
%fs <command> /<path>
```

ドライバーのボリュームがデフォルトとなるコマンドを使用する際、パスの先頭に`/dbfs`を追加する必要があります。

```bash:Bash
%sh <command> /dbfs/<path>/
```

```py:Python
import os
os.<command>('/dbfs/<path>')
```

# ドライバーのファイルシステムのファイルにアクセスする

ドライバーストレージがデフォルトのコマンドを使用する際、相対パスあるいは絶対パスを指定することができます。

```bash:Bash
%sh <command> /<path>
```

```py:Python
import os
os.<command>('/<path>')
```

DBFSルートがデフォルトのコマンドを使用する際は、`file:/`を使用する必要があります。

```py:Python
dbutils.fs.<command> ("file:/<path>")
```

```bash:Bash
%fs <command> file:/<path>
```

これらのファイルはアタッチされたドライバーノードのボリュームに存在しており、Sparkは分散処理エンジンであるので、全てのオペレーションがここにあるデータに直接アクセスできる訳ではありません。ドライバーのファイルシステムからDBFSにデータを移動する必要がある場合には、マジックコマンドあるいはDatabricksユーティリティを用いてファイルをコピーすることができます。

```py:Python
dbutils.fs.cp ("file:/<path>", "dbfs:/<path>")
```

```bash:Bash
%sh cp /<path> /dbfs/<path>
```

```bash:Bash
%fs cp file:/<path> /<path>
```

# サンプルを通じてデフォルトロケーションを理解する

このセクションで説明されたコマンド、および、いつそれぞれの文法を使うべきかを要約、説明するテーブルと図となります。

| コマンド | デフォルトロケーション | rootからの読み込み | ローカルファイルシステムからの読み込み |
|:--|:--|:--|:--|
|`%fs`   | DBFSルート  |   | パスに`file:/`を追加  |
|`%sh`  | ローカルドライバーノード  | パスに`/dbfs`を追加  |   |
|`dbutils.fs`   | DBFSルート  |   | パスに`file:/`を追加  |
|`os.<command>`や他のローカルコード   | ローカルドライバーノード  | パスに`/dbfs`を追加  |   |
|`spark.[read/write]`   | DBFSルート  |   | サポートされていません  |

![](https://docs.databricks.com/_images/dbfs-and-local-file-paths.png)

```bash:Bash
# %fsのデフォルトロケーションはDBFSルートです
%fs ls /tmp/
%fs mkdirs /tmp/my_cloud_dir
%fs cp /tmp/test_dbfs.txt /tmp/file_b.txt
```

```py:Python
# dbutils.fsのデフォルトロケーションはDBFSルートです
dbutils.fs.ls ("/tmp/")
dbutils.fs.put("/tmp/my_new_file", "This is a file in cloud storage.")
```

```sh:Bash
# %shのデフォルトロケーションはローカルファイルシステムです
%sh ls /dbfs/tmp/
```

```py:Python
# osコマンドのデフォルトロケーションはローカルファイルシステムです
import os
os.listdir('/dbfs/tmp')
```

```bash:Bash
# %fsとdbutils.fsを使用する際、ローカルファイルシステムから読み込むためには file:/ を使う必要があります
%fs ls file:/tmp
%fs mkdirs file:/tmp/my_local_dir
dbutils.fs.ls ("file:/tmp/")
dbutils.fs.put("file:/tmp/my_new_file", "This is a file on the local driver node.")
```

```bash:Bash
# %shはデフォルトでローカルファイルシステムから読み込みを行います
%sh ls /tmp
```

# マウントされたオブジェクトストレージのファイルにアクセスする

DBFSにオブジェクトストレージをマウントすることで、ローカルファイルシステムに存在するかのようにオブジェクトストレージのオブジェクトにアクセスすることができます。

```py:Python
dbutils.fs.ls("/mnt/mymount")
df = spark.read.format("text").load("dbfs:/mymount/my_file.txt")
```

# ローカルファイルAPIの制限

以下では、DatabricksランタイムのFUSEにおけるローカルファイルAPI使用時の制限を列挙します。

- クライアントサイド暗号化が有効化されたAmazon S3マウントをサポートしていません。
- ランダムの書き込みはサポートしていません。ランダムの書き込みが必要となるワークロードにおいては、以下のようにローカルディスクでオペレーションを行い、`/dbfs`に結果をコピーするようにしてください。

```py:Python
# python
import xlsxwriter
from shutil import copyfile

workbook = xlsxwriter.Workbook('/local_disk0/tmp/excel.xlsx')
worksheet = workbook.add_worksheet()
worksheet.write(0, 0, "Key")
worksheet.write(0, 1, "Value")
workbook.close()

copyfile('/local_disk0/tmp/excel.xlsx', '/dbfs/tmp/excel.xlsx')
```

- スパースファイルはサポートされていません。スパースファイルをコピーするには、`cp --sparse=never`を使用してください。

```bash:Bash
$ cp sparse.file /dbfs/sparse.file
error writing '/dbfs/sparse.file': Operation not supported
$ cp --sparse=never sparse.file /dbfs/sparse.file
```

### Databricks 無料トライアル

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