Webシステムにおいて「画像や帳票等のファイルはDBへ格納すべきなの？」を調べてみた（ファントムファイル）

この記事は、 JPOUG Advent Calendar 2023 24日目の記事です。
23日目は multilayer さんの記事『[OCIのLanding Zoneについて調べてみた！](https://zenn.dev/multilayer/articles/a0823304fe8f28)』でした。

## 想定読者
ファントムファイルについてよく知らない、帳票の扱い方をあまり考えたことがない人

## イントロダクション

皆さん、世の中のWebシステムで利用される画像や帳票ファイルがどこに保存されているかご存知でしょうか？

帳票や大きな画像ファイルなどを扱う際、大きく分けて２つの設計方針があります。
・DBに直接保存する
・DB外部に保存し、パスなどをDBに保存する

***オライリーのSQLアンチパターン***の、”ファントムファイル”という章にはこのあたりのことが書いています。

[[Amazonへのリンク:オライリージャパン SQLアンチパターン]](https://www.amazon.co.jp/SQL%E3%82%A2%E3%83%B3%E3%83%81%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3-Bill-Karwin/dp/4873115892/ref=sr_1_1?adgrpid=92275166110&gclid=CjwKCAiAhJWsBhAaEiwAmrNyq-dfdNqp_9kU0toEVJb76kysGKKIXkOOuofnESsphMx1p4yBgKqwFhoCNQYQAvD_BwE&hvadid=651019732713&hvdev=c&hvlocphy=1009307&hvnetw=g&hvqmt=b&hvrand=12299955928948352422&hvtargid=kwd-374359839989&hydadcr=14991_13514080&jp-ad-ap=0&keywords=sql%E3%82%A2%E3%83%B3%E3%83%81%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3&qid=1703316501&sr=8-1)

しかし、オライリーの内容に違和感を持ち自分なりに調べていくと、監訳者すら内容を否定するようなお話となっていたため、調べたことを記事にしてみました。

検証環境もいくつか作っているので、本来は検証的なところに触れたかったのだけが非常に残念。時間があったら続編でも書きたいなというところです。。。


注意：
本記事は学びの途中経過をアウトプットした記事になりますので、私見入り乱れたゆるい記事であることに承知いただける方のみお読みください・・

## ファントムファイルの概要

★オライリーで紹介されているファントムファイル（幻のファイル）って何？

画像や帳票ファイル（PDF等）などを「ファイルシステムに保存」し、DBには「保存先のパスを保存する」設計で実装したことにより、障害時/実装考慮不足/メンテ不足をきっかけにして「***DBにファイルシステムを指すフルパスはあるがファイルシステム上にファイルがない***」や「***ファイルシステムにファイルはあるが、DBにフルパスがない***」などの不整合が起きてしまうアンチパターンな状態。

<参考リンク>
1)SQLアンチパターンをもとに、ファントムファイルの解説記事：

https://qiita.com/fktnkit/items/cc811929485471897c89

2)２つの設計との概要とその実装案

https://bluerabbit.hatenablog.com/entry/2015/03/29/214639

## DBの外にファイルを保存する設計はアンチパターンじゃない
DBの外（ファイルシステムやオブジェクトストレージなど）にファイルを保存する設計はアンチパターンではありません。

オライリー本では「ファントムファイル」の言葉の定義が定かではありませんが、語感や文章から、
「実装考慮が足りずにファイルシステムに帳票を保存する設計を取ることで***不整合が起きうる状態***」　をファントムファイルと***揶揄***しているだけで、
「実装考慮が十分で、正しくファイルシステムに帳票を保存する設計 」のことはファントムファイルと呼んでいないと思っています。

それどころか、「大多数のプログラマーの間では、ファイルを常にデータベースの外部に格納すべきであるという考えで意見が一致しています。」と書かれています。つまり、DBにファイル本体を保存する設計は少数派であることを認めています。

どんな記事を調べてもたいてい、性能観点(≒システムのコスパ)からファイルシステムに保存することを推奨していました。

## 「DBにファイルを突っ込むシステム」における負荷試験の実体験

私自身、「DBに数百MBの帳票ファイル本体を突っ込んでおり、クライアントからダウンロードすることが多いシステム」への負荷（性能）試験で配信シナリオのパフォーマンスの悪さに苦い経験をしたことがあります・・・。

このシステムはWebシステム全体（Web/AP/DB）のパラメータ設計などが非常に残念な状態だったため、まずは明らかに眼の前にある問題を片っ端から片付けよう。というアプローチで案件を進め、なんとかなりました。

しかし、最後までかなり低いTPSで詰まっていましたし、全く良いイメージはありません。

このとき、開発チームの一人から「DBに帳票本体を入れる設計ってどうなんですか？」というどストレートな質問を受けましたが、私はその回答を出せるスキルも工数も持っていませんでした。あの頃の宿題への対応というような本記事です。

そして、私は古き良きSIer勤めであるためか、DBに帳票本体を入れる設計を取っているシステムを連続で担当しています。セキュリティなどが重視されるシステムを担当しがちなSI業界ではこの設計はWeb系よりも多いのかもしれませんね。

## 有識者たちの議論

監訳を務めている和田さんのツイートや、有識者たちの議論

https://twitter.com/t_wada/status/838724919831605249

https://twitter.com/t_wada/status/805598846394863617

https://bash0c7.hatenablog.com/entry/2015/04/11/153249

https://twitter.com/soudai1025/status/989337828843638784

https://twitter.com/mamy1326/status/884371531215675392


## ２つの設計に関連する「性能へのファクター」を考えてみた

理想はDBの外に置いて、しっかり実装を検討することなのだろう。

それでも私は「DBにBLOBで突っ込んでいる設計で実装したシステムへの性能支援」をする場合があります。

実際にシステムと対峙したときに「設計を変えるべきかと思う」、「そのままの設計で、パラメータなどをチューニングしよう」どういったアドバイスをするべきか判断できるようにならなければいけません。

その時のために、２つの設計の違いにおける、パフォーマンスに対するファクターを調べてみました。

***ファクター１：ファイルサイズの違いと断片化性能***
以下の研究を見つけました。やはりこの話題では、ファイルサイズが最も重要なようです。

Microsoftさんの「To BLOB or Not To BLOB:Large Object Storage in a Database or a Filesystem?」です。

[Microsoft Research : 「To BLOB or Not To BLOB : Large Object Storage in a Database or a Filesystem?」](https://arxiv.org/pdf/cs/0701168.pdf)

下手に私が解説すると情報の正しさが下がってしまいます。ぜひ読んでみてください。
2006年の古い資料ですし、現代のストレージやファイルシステムの実装とは異なるでしょう。

しかし、「ファイルサイズが大きい場合、DBよりファイルシステムが得意なのかな？」っていう雑に捉えるくらいは問題は無いような気がしています。もちろん「ファイルサイズの損益分岐点」は時代によって異なるでしょう。

Microsoftさんの研究なのでもちろんSQLServerでの記事です。
Oracle公式さんのこれ系の記事必死に探したが見つからず・・・。どなたか見つけたら共有ください！

***ファクター２：キャッシュ設計を検討***

パブリッククラウドを学んでいると絶対に出てくる、「ObjectStorageに帳票おいてCDN経由で配信」する設計。クラウドのシステムならこの設計使うべきなのかなと思います。

※注：2023/12現在、OCI製のCDNはなかったり、ObjectStorageで静的ホスティングは工夫が必要だったりすると思います。

https://docs.oracle.com/ja/learn/oci-api-gateway-web-hosting/index.html#introduction

つまり、DBに直接突っ込むべきシステムとは、「ObjectStorage/CDNでの配信設計を取れない場合、かつ、特に性能を気にしない or 実装コスト（や堅牢性）重視の場合 or 扱うファイルサイズが十分小さい場合」が現実的な選ばれ方なのかと感じています。

https://x.com/guitarrapc_tech/status/1719951307149369527?s=20

また、当たり前ですがDBに帳票を突っ込む場合、DBのメモリ（キャッシュ）設計に余裕を持つことも大きなファクターだと考えます。

DBのメモリサイジング時、この２つの設計方針の選び方をあまり考慮に入れずDBのメモリをサイジングしてしまうと品質低下につながるのでは・・・？

***ファクター３：シナリオ（帳票のワークロード）の違い***

一口に帳票と言っても、色々名使われ方があるでしょう。
配信（クライアントが帳票をダウンロードする）シナリオが多い場合/アップロードシナリオが多い場合/一度保存されたファイルがガリガリ書き換えられる場合/同時アクセス（TPS）数は高いのか低いのか、など。

システムによって帳票へのアクセスの状況は異なり、性能要件ももちろん異なります。
それによって取るべき設計が変わると思います。

一度保存されたあとはほとんどアクセスされずにアーカイブされているだけだよ。であれば性能を気にすることはあまりないので、実装コストを下げるためにDBに入れることを許容する、なんて考えもありなのかなと考えています。

***ファクター４：ファイルシステムの種類の違い***

当たり前ですが、DBはファイルシステム上にインストールします。
特に要件がない場合、OSS系（MySQLやPostgreSQL）はOSのデフォルトであるファイルシステムを利用することが多いんじゃないでしょうか。

例えばRHELだとRHEL6系はext系のファイルシステム、RHEL7/8/9系はXFSと変化がありました。

OracleではASM(ACFS/ADVM)があったり、DBFS(BLOBなどを扱うインターフェース)があったり、Oracle独自色が強いのかなと考えています。

また、（おそらく他のDBMSでも）LOBデータは普通のテーブルとは異なるブロック（ページ）数の単位、チャンクの単位で管理されています。つまりは、DBMSごとにLOBの性能（レスポンスやTPS(同時実行性能)など）に色が出る可能性があると考えています。

これらDBMSの実装の違いから、Oracleは他のDBMSと比べ「ファイルサイズの損益分岐点」が他DBと異なる可能性もあるのか？と思っています。
”DBMSごとのBLOB性能の基礎値取得”など検証したら面白そうです。

参考：SecureFiles and Large Objects Developer's Guide（LOBのパフォーマンスガイドライン）

https://docs.oracle.com/en/database/oracle//oracle-database/23/adlob/performance-guidelines.html#GUID-0EA0E7B7-96E6-4393-B2DA-1F2CA72A72B7


***ファクター５：言語/WebフレームワークやDBのBLOB関数、その性能***

帳票を扱う場合にフレームワークやDBの関数、つまりはファイルとのインターフェース部分により性能差はもちろんあるでしょう。

私は全く詳しくないところですが、注意すべき扱い方などはあるのかもしれません。

## その他Tips：NoSQLの選択肢
本記事はRDBを前提として書きましたが、MongoDBなどNoSQLを格納先と考えると話はがらっと変わると思います。

参考：以下の記事の「SECOND CHOICE – NOSQL DB」をご覧ください。この記事の著者もDBに保存することは効率的ではないため、ファイルシステムを利用し、第２の選択肢としてNoSQLを提案しています。

https://lunaticthinker.me/index.php/where-to-store-uploaded-files-file-system-versus-database-storage/


## まとめ

オライリー本に書いていることとは逆とも取れる、「DBの外部に置くこと」が第一の選択肢として推奨する人が圧倒的に多いと感じました。

また、クラウド時代の現在はDBの外に保存する設計として、「オブジェクトストレージに配置し、CDN経由で配信」の推奨派が多いと思います。（クラウドベンダーがそう推奨しているでしょう）

「DBに入れる設計」に向いているシステム（堅牢性や実装コストをとる）こそ、全てのシステムの中の割合としては少数派な気がしていて、性能観点でアンチパターンな設計となりうる可能性が高いと言えてしまうのでは？とまで感じています。

この「設計方針の選択」についてはDB有識者だけではもちろん考えることができず、しかしアプリ（設計や利用状況）有識者だけでも難しい話題だと思いました。全体を俯瞰した知識と、***環境ごとの検証***が必要だと思われます！（本書でも常に2つの方針を検討しよう、と明記されてます。1つの方針の検討だけで実装してしまうのがアンチパターン、なら賛成！）

私はまだキャリアが短いため、たくさんの有識者が2016年〜2018年あたりに多く議論をしていることも初めて知ることができました。

誤りや指摘、考え方のアドバイスなどがあればお気軽にお願いします！

## そのほかの参考記事：

https://qiita.com/nakasato310/items/e82c6431f526b327291c

https://www.linkedin.com/pulse/which-better-saving-files-database-file-system-abuthahir-sulaiman

https://www.linqz.io/2019/07/should-i-store-file-in-database.html

https://www.brentozar.com/archive/2021/07/store-files-in-a-file-system-not-a-relational-database/