二進製文件應該儲存在數據庫中嗎?
儲存與數據庫中的數據相關的二進製文件的最佳位置是什麼?你應該:
- 使用 blob 儲存在數據庫中
- 使用數據庫中的連結儲存在文件系統上
- 儲存在文件系統中,但重命名為內容的雜湊並將雜湊儲存在數據庫中
- 我沒有想到的東西
(1) 的優點是(除其他外)事務的原子性得以保留。代價是您可能會顯著增加儲存(以及相關的流/備份)需求
(3) 的目標是在一定程度上保持原子性 - 如果您可以強制您正在寫入的文件系統不允許更改或刪除文件,並且始終具有正確的雜湊作為文件名。這個想法是在允許引用雜湊的插入/更新之前將文件寫入文件系統 - 如果此事務在文件系統寫入之後但在數據庫 DML 之前失敗,那很好,因為文件系統“偽造”是所有的儲存庫可能的文件和雜湊 - 是否有一些文件沒有被指向並不重要(如果你小心的話,你可以定期清理它們)
編輯:
看起來一些 RDBMS 以各自的方式涵蓋了這一點 - 我很想知道其他人是如何做到的 - 特別是在 postgres 的解決方案中
- 使用 blob 儲存在數據庫中
一個缺點是它會使您的數據庫文件非常大,並且可能太大而無法使用現有設置進行備份。一個優點是完整性和原子性。 2. 使用數據庫中的連結儲存在文件系統上
我在這樣做時遇到瞭如此可怕的災難,人們一直在建議這樣做,這讓我感到害怕。一些災難包括:
- 一位特權使用者會重新排列文件並經常斷開數據庫中路徑與它們現在所在位置之間的連結(但不知何故這成了我的錯)。
- 當從一台伺服器移動到另一台伺服器時,一些文件的所有權失去了,因為舊機器的管理員帳戶(舊網站正在執行的)的 SID 不是域的一部分,因此複製的文件具有 ACL,可以無法解決,從而向使用者顯示使用者名/密碼/域登錄提示。
- 一些路徑最終超過了 256 個字元,
C:\
並且.doc
不是所有版本的 NT 都能夠處理長路徑。
- 儲存在文件系統中,但重命名為內容的雜湊並將雜湊儲存在數據庫中
根據我對上述場景的解釋,我工作的最後一個地方是這樣做的。他們認為這是組織無法獲得大型數據庫經驗(任何大於約 40G 的東西都被規定為“太大”)、公司無法購買大型硬碟驅動器以及無法購買更現代的後背之間的折衷。解決方案,以及擺脫我上面確定的風險 #1 和 #3 的需要。
我的觀點是,在數據庫中儲存為 blob 是一種更好的解決方案,並且在多伺服器場景中更具可擴展性,尤其是考慮到故障轉移和可用性問題。
不要將文件儲存在數據庫中。
每個人,無一例外,可以執行市場上任何 RDBMS 的人都已經有了專門用於儲存文件的數據庫,而 RDBMS 本身也在使用它!該數據庫是文件系統。現在讓我們談談將文件儲存在數據庫中的一些潛在缺點,以及將文件儲存在數據庫中的一些特定緩解因素。
**數據庫中的文件沒有文件句柄。**這是什麼意思?
- 程序員談話:你不能尋找(
fseek
),沒有能力通過非同步訪問(asyncio
或epoll
)來管理資源,沒有sendfile
(從核心空間中保存副本)。- 實際應用:想通過HTTP2/3向客戶端發送影片或圖片?如果它在數據庫中,那麼您首先必須查詢它。對於返回該文件的任何查詢,您都必須等待整個查詢結束,然後該文件才能進入下一步。在與 Web 伺服器不同的伺服器上使用 rdbms 進行生產安裝時,您首先必須將文件完全從 rdbms 傳輸到 Web 伺服器,而不是通過流式傳輸。但是,如果傳輸層提供了文件系統抽象(甚至 NFS 也支持),您可以在文件中尋找一半並立即開始將其流式傳輸回客戶端,而無需緩衝任何多餘的文件。這通常由 Web 伺服器完成nginx、Apache、PureFTP 和 ProFTP。
**在 RDBMS 上進行雙重複制。**事實上它在數據庫中,你可能會寫兩次。一次進入預寫日誌 (WAL),然後再次進入表空間。
沒有更新,永遠 MVCC意味著什麼都沒有更新,只是通過修改重新複製,然後舊行被標記為過期(刪除)。對文件的任何更新都需要寫入整行,而不僅僅是整行文件。文件系統也可以通過數據日誌提供此功能,但您很少需要它。
文件讀取和傳輸以減慢查詢如果文件本身儲存在您需要查詢的行上,則整行要麼必須等待文件傳輸,要麼您必鬚髮出兩個單獨的查詢.
DB 客戶端上的**記憶體使用情況。**DB 客戶端(libpq、jdbc、odbc、freetds 等)等可能會在記憶體中緩衝查詢。當記憶體緩衝區耗盡時,它可能會啟動磁碟緩衝區,或者更糟糕的是,它可能會回退到核心以分頁到磁碟。
查詢限制許多數據庫提供了在查詢佔用過多時間或資源時終止和獲取查詢的能力。請記住,文件傳輸不會在任何實施中逐項列出。該查詢是否在 3 秒後被終止?還是需要 1 秒,後端花了 2 秒傳輸文件?不僅僅是“逐項”,當 99.9% 的查詢返回 1 KB 而另一個返回 1 GB 時,您將如何有效地說明查詢應該花費多少時間?
無寫時復製或去重XFS 和 BTRFS 透明地支持寫時複製和去重。這意味著在任何地方都有相同的圖片,或者需要它的第二個副本可以由文件系統透明地處理。但是,如果文件不是獨立存在的,並且位於一行或儲存中,則文件系統可能無法對其進行重複數據刪除。
誠信很多人在這裡都在談論誠信。您認為在檢測文件系統損壞、使用文件系統的應用程序或文件系統的核心實用程序方面哪個更好?將文件連續儲存或離線儲存,任何文件系統損壞都將掩蓋數據庫。
xfs_repair
當你有文件系統或硬碟驅動器損壞時,它非常擅長恢復,如果它失敗了,進行數據取證仍然會容易得多。雲遷移如果您想將文件儲存在 SAN 或云上,您將遇到更多困難,因為現在儲存遷移是數據庫遷移。例如,如果您的文件儲存在文件系統上,您可以相當輕鬆地將它們移動到 S3(並且使用類似的東西
s3fs
可以是透明的)。例外
在數據庫中儲存文件有一些有效的案例,
- 當您需要過渡編輯文件時。這意味著編輯文件實際上是您交易的一部分。或者,如果事務因關係(表)中的數據完整性問題而失敗,您需要能夠回滾對文件的編輯。
- 當您需要確保文件系統使用數據進行精確的版本控制並且您無法承擔保持它們同步的任何風險時。
- 當您數據庫實際上可以解析文件並且您可以查詢它時。例如,在 PostgreSQL 中,拓撲可以是 PostGIS 的查詢。此時,雖然它是一個文件,但它也是查詢的數據,而不是儲存轉儲。
緩解措施
某些數據庫具有“外部管理資源”的概念,其中數據庫要麼私下管理磁碟上的文件,例如
- PostgreSQL 通過大對象基礎設施在事務期間為資源提供文件句柄。
- SQL Server 2017 的文件流基礎結構提供了一種在事務期間持續的臨時訪問,您可以使用它來獲取文件路徑並打開文件句柄。
- Oracle 提供
BFILE
(這與他們內部稱為 LOB 的東西無關SecureFile
一些數據庫離線或可以儲存大型二進制對象,例如 Oracle SecureFile。這允許您更新行,而無需重寫文件。
一些數據庫(如 Oracle)在沒有 WAL 日誌的情況下執行其 MVC,並且不必重複寫入文件。
一些數據庫,如 SQL Server 和 Oracle,提供了從文件中“流式傳輸”數據的能力,而無需文件句柄。這可能會或可能不會在與數據庫查詢不同的連接上執行。但這裡的關鍵是,雖然您可以流式傳輸文件(理論上),但我找不到任何證據證明不是由使用該功能的提供商生產的任何產品。例如,允許您執行此操作的 NGINX/Apache 橋接器在哪裡?
Oracle 通過內部 LOB 儲存(如 SecureFile)提供可選的重複數據刪除、壓縮和加密。
結論
當您將文件放入數據庫時,最壞的情況是對性能和與工具的兼容性*非常不利。*它總是非常依賴於實現。數據庫在文件系統方面絕不比文件系統更好。在任何方面,它都是一種妥協,即使您獲得了強大的緩解功能(例如 SecureFile 的情況),工具也很差,以至於它實際上只不過是一個行銷點,除非您的整個堆棧是由 RDBMS 提供商建構的。
保持簡單,一般規則是將文件保留在 DB 之外。
解決方案
您應該如何儲存文件或以這種方式抽象文件系統以有效地為多個租戶和使用者執行?我偏愛散列文件內容。這些天來這很常見並且效果很好。