一起使用 MongoDB 和 PostgreSQL
我目前的項目本質上是一個工廠文件管理系統的執行。
也就是說,有一些皺紋(驚喜,驚喜)。雖然有些問題是針對該項目的,但我相信有一些普遍的觀察和問題已經出現,它們沒有一個規範的答案(無論如何我都能找到)並且適用於更廣泛的問題領域. 這裡有很多,我不確定它是否適合 StackExchange Q&A 格式,但我認為它 a) 是一個可以回答的問題,b) 不夠具體,它可以使社區受益。我的一些考慮是針對我的,但我認為這個問題可能對任何面臨決定 SQL 與 NoSQL 與兩者兼而有之的人有用。
背景:
我們正在建構的 Web 應用程序包含本質上明顯是關係的數據以及面向文件的數據。我們也想吃蛋糕。
TL;DR:我認為下面的#5 通過了氣味測試。你?有沒有人有在單個應用程序中集成 SQL 和 NOSQL 的經驗?我試圖在下面列出此類問題的所有可能方法。我錯過了一個有前途的選擇嗎?
複雜性:
- 有許多不同類別的文件。這些要求已經要求提供數十種不同的文件。這個數字只會上升。最好的情況是我們可以利用簡單的領域特定語言、程式碼生成和靈活的模式,這樣領域專家就可以處理新文件類的添加,而無需 DBA 或程序員的干預。(注意:已經知道我們正在遵守 Greenspun 的第十條規則)
- 以前成功寫入的完整性是項目的核心要求。這些數據對業務至關重要。如果成功寫入的內容保持寫入狀態,則可以犧牲寫入的完整 ACID 語義。
- 文件本身很複雜。在我們的具體案例中,原型文件將需要每個文件實例儲存 150 多個不同的數據。病理情況可能會更糟一個數量級,但肯定不是兩個。
- 單一類別的文件是一個移動的目標,會在稍後的時間點進行更新。
- 當我們將 Django 連接到關係數據庫時,我們喜歡從 Django 獲得的免費內容。我們希望保留免費贈品,而不必跳回兩個 Django 版本來使用 django-nonrel 分支。完全轉儲 ORM 比降級到 1.3 更可取。
從本質上講,它是關係數據(您的典型 Web 應用程序,如使用者、組等,以及我們需要能夠實時對複雜查詢進行切片和切塊的文件元數據)和文件數據(例如我們沒有興趣加入或查詢的數百個欄位 - 我們唯一的數據案例將用於顯示輸入它的單個文件)。
我想對我的首選方法進行完整性檢查(如果您檢查我的發帖歷史,我非常清楚我不是 DBA 的事實),並列舉我遇到的所有選項以供其他人解決涉及關係和非關係數據的大體相似的問題。
建議的解決方案:
1. 每個文件類一張表
每個文件類都有自己的表,其中包含所有元數據和數據的列。
優點:
- 標準 SQL 數據模型正在發揮作用。
- 以最佳方式處理關係數據。如果需要,我們稍後會去規範化。
- Django 的內置管理界面很適合自省這些表,並且 ORM 可以愉快地使用 100% 開箱即用的數據。
缺點:
- 維護噩夢。數十個(數百個?)具有(數十個?)數千列的表。
- 負責確定要寫入哪個表的應用程序級邏輯。將表名作為查詢參數是很糟糕的。
- 基本上所有業務邏輯更改都需要架構更改。
- 病理情況可能需要跨多個表對單個表單進行條帶化數據(請參閱:PostgreSQL 表中的最大列數是多少?)。
- 我們可能需要去找一個真正的、對上帝誠實的 DBA,他無疑最終會憎恨生活和我們。
2. EAV 建模
只有一個欄位表。實體-屬性-值建模已經很好理解了。為了完整起見,我已將其包括在內。我認為 2013 年開始的任何新項目都不會故意採用 EAV 方法。
優點:
- 易於建模。
缺點:
- 更難查詢。
- DB 層不再直接表示構成一個應用程序級對象的內容。
- 我們將失去 DB 級別的約束檢查。
- 一張表上的行數將增長 100-1000 倍。可能是未來的痛點,性能方面。
- 可能的索引有限。
- 就 ORM 而言,DB 模式是無意義的。包含電池的網路應用程序內容被保留,但自定義數據模型將需要自定義查詢。
3. 使用 PostgreSQL hstore 或 json 欄位
這些欄位類型中的任何一種都可以在關係數據庫的上下文中儲存無模式數據。我沒有立即跳到這個解決方案的唯一原因是它相對較新(在 8.4 版中引入,所以不是那麼新),我以前接觸過它為零,我很懷疑。出於完全相同的原因,我覺得它是錯誤的,我會因為將所有漂亮、容易標準化的數據放入 Mongo 感到不安——即使 Mongo 可以處理文件之間的引用。
優點:
- 我們獲得了 Django ORM 以及內置的身份驗證和會話管理的好處。
- 一切都保留在我們之前成功用於其他項目的一個後端中。
缺點:
- 個人沒有這方面的經驗。
- 它看起來不是一個非常常用的功能。看起來他們被推薦給關注 NOSQL 解決方案的人很多,但我沒有看到很多證據表明他們被選中。這讓我覺得我一定錯過了什麼。
- 儲存的所有值都是字元串。失去 DB 級別的約束檢查。
- 除非使用者專門查看文件,否則 hstore 中的數據將永遠不會顯示給使用者,但儲存在更標準列中的元數據將會顯示。我們將擊敗元數據,我擔心我們將創建的相當大的 hstore 可能會帶來性能缺陷。
4. 全面面向文件
將所有東西都記錄下來(在 MongoDB 的意義上)。創建一個類型的集合
Document
併收工。將所有外圍數據(包括使用者帳戶、組等數據)也帶入 mongo。這個解決方案顯然比 EAV 建模更好,但我感覺不對,原因與 #3 感覺不對——他們都覺得你也想用你的錘子作為螺絲刀。優點:
- 無需預先對數據建模。收集一個包含類型文件的集合,
Document
然後收工。- 已知良好的縮放特性,如果集合需要增長到包含數百萬甚至數十億的文件。
- JSON 格式 (BSON) 對開發人員來說很直覺。
- 據我了解(目前僅含糊不清),通過對寫入關注級別的偏執,即使是單個實例也可以在任何情況下提供非常強大的數據安全性,直到硬碟驅動器崩潰。
缺點:
- ORM 是 Django 主幹的視窗外。隨它一起消失的免費贈品:身份驗證框架、會話框架、管理界面,當然還有很多其他的東西。
- 必須使用 mongo 的引用功能(需要多個查詢)或非規範化數據。我們不僅失去了從 Django 獲得的免費贈品,我們還失去了我們在 PostgreSQL 中認為理所當然的 JOIN 之類的免費贈品。
- 數據安全。當人們閱讀 MongoDB 時,似乎總是至少有一個人提到它將如何啟動和失去您的數據。他們從不引用一個特定的事件,這可能只是胡說八道,或者只是與舊的預設火災有關,忘記了寫關注,但它仍然讓我擔心。我們當然會在任何情況下使用一種相當偏執的備份策略(如果數據被無聲地損壞,那當然很可能是無關緊要的……)。
5. PostgreSQL 和 MongoDB
關係數據進入關係數據庫,文件數據進入面向文件的數據庫。關係數據庫上的
documents
表包含我們可能需要索引或切片和切塊的所有數據,以及當我們需要查詢文件上欄位的實際值時將使用的 MongoDB ObjectId。我們將無法使用 ORM 或內置管理員來獲取文件本身的值,但這並不是什麼大的損失,因為整個應用程序基本上是文件的管理界面,我們可能不得不將 ORM 的特定部分定製到不可接受的程度,以使其按我們需要的方式工作。優點:
- 每個後端只做它擅長的事情。
- 模型之間的引用被保留,無需多次查詢。
- 就使用者、會話等而言,我們可以保留 Django 給我們的電池。
documents
無論創建多少不同類別的文件,都只需要一張表。- 查詢頻率較低的文件數據與查詢頻率高得多的元數據緊密分離。
缺點:
- 檢索文件數據將需要 2 個順序查詢,首先針對 SQL DB,然後針對 MongoDB(儘管這並不比將相同數據儲存在 Mongo 中且未進行非規範化更糟糕)
- 寫作將不再是原子的。保證對單個 Mongo 文件的寫入是原子的,而 PG 顯然可以保證原子性,但確保兩者之間的寫入原子性將需要應用程序邏輯,這無疑會降低性能和復雜性。
- 兩個後端 = 兩種查詢語言 = 兩個具有不同管理要求的不同程序 = 兩個數據庫爭奪記憶體。
一些想法……
通常,人們不想在不同的系統中儲存緊密相關的資訊。事情不同步的可能性很大,現在你手上的問題不是一個,而是兩個。不過,您可以使用 Mongo 做的一件事是使用它來管道輸入或輸出數據。我的偏好是盡可能將所有內容保留在 PostgreSQL 中。但是,我要指出,這樣做確實需要 PostgreSQL 程式方面的專業知識,並且不適合不願致力於使用高級功能的商店。我看到一組與您有所不同的選項。由於我的偏好不是我看到的列表,我會把它給你。
您可能可以將元數據分為公共數據、類所需的數據和文件數據。在這方面,您將擁有一個包含基本公共資訊的通用目錄表,以及每個類一個表。在此表中,您將有一個 hstore、json 或 xml 欄位,這些欄位將儲存其餘數據以及儲存必須顯著約束的數據的列。這將減少您需要在每個類中放入這些表的內容,但允許您根據需要利用約束。這三個選項有不同的問題,值得分別考慮:
hstore相對有限,但也被很多人使用。它不是很新,但它只是一個鍵/值儲存,並且不像 json 和 xml 那樣具有嵌套資料結構的能力。
json是相當新的,現在並沒有真正做很多事情。這並不意味著你不能用它做很多事情,但你不會做很多開箱即用的事情。如果你這樣做了,你可以期望做大量的程式,可能在 plv8js 中,或者,如果你想堅持使用舊環境,plperlu 或 plpython。
json
雖然至少在目前的開發快照中,但在 9.3 中得到了更好的支持,因此當該版本發佈時,情況會變得更好。xml是三者中支持最好的,功能最多,支持歷史最長。再說一次,它是 XML ……
但是,如果您決定同時使用 Mongo 和 PostgreSQL,請注意 PostgreSQL 支持 2 階段送出,這意味著您可以執行寫入操作,然後發出
PREPARE TRANSACTION
,如果成功,則在 Mongo 中進行原子寫入。如果成功了,那麼你可以COMMIT
在 PostgreSQL 中。
您可以設置一個查詢引擎,例如Presto或Dremio,以通過單個查詢連接 MongoDB 和 Postgres 中的數據。兩者都有用於每個數據庫的連接器(請參閱此處和此處的文件),並分別建議執行“任何事物上的 SQL”和“加入任何事物”。
要測試 Presto,您可以使用 Hadoop、Hive 和 Presto 在 AWS EMR 上部署一個小型集群(如果您不想使用命令行,請添加 Hue),它可以直接使用 - 請確保按照這些說明進行設置連接器。Hive 不是絕對必要的,但有了它,您可以使用 Mongo 和 Postgres 之間的連接結果創建表(查看此頁面以獲取範例)。市場上還有一個付費版本,它(據說)經過大量優化,並有 30 天的試用期。
我沒有使用過 Dremio,但也有一些簡單的方法可以在 AWS、Azure 或本地部署它。他們的網站上有一些線上課程,可以訪問“虛擬實驗室”,您可以使用它免費學習課程。