如何選擇數據庫,生活中還有比 SQL 更重要的東西
無論你是在建立你的第一個還是第五十個應用程序,選擇一個數據庫是最困難的選擇之一。這將是你做出的第一個決定之一,也可能是在你的應用程序中根深蒂固的一個決定。由於有大量優秀的數據庫可用,而且有大量現成的信息可用,這隻會使任務更加困難。
我們最近的項目,以及 WeGift 對發展速度的渴望,使得我們有必要轉變爲事件驅動的架構,這爲利用最新的技術提供了很多機會。對於我們最近的項目,我們必須爲一個有多個第三方集成的系統選擇一個數據庫,它將返回不同的元數據,能夠促進大量的交易。
在這篇文章中,我將簡明扼要地介紹常見的數據庫類型,它們的用途,以及選擇數據庫時應該考慮的一些不太常提到的問題。
數據庫的主要類型有哪些?
關係型數據庫
例子。PostgreSQL、MySQL、Oracle DB
關係型數據庫是最常用的數據庫類型。數據被組織在表格中,毫不奇怪,它們之間可以使用外鍵定義關係。SQL 允許你查詢這些表並將它們連接起來,允許你以適合你要求的格式有效地檢索數據。表的結構是使用列和行,其中列定義了數據屬性,行定義了表中的一條記錄。
圖 1:關係型數據庫中的表的例子
關係型數據庫確保了 ACID 交易,這意味着數據的完整性總是被保留下來。ACID 事務,再加上在數據插入之前需要定義模型,意味着當數據的一致性和穩定性是行爲要求的組成部分時,關係型數據庫是完美的。然而,這種穩定性是以靈活性爲代價的。
關係型數據庫的主要缺點是,當需要大量交易時,或者數據庫的地理位置很重要時,性能的可擴展性。典型的用於擴展關係型數據庫的第一個端口是垂直擴展,增加更多的計算能力。然而,這當然有其侷限性。除此之外,還需要引入分片、緩存和讀取副本的使用,但這導致瞭解決這些問題的額外工作,而這些工作往往只有在數據庫明顯成爲瓶頸時纔會被意識到。
關係型數據庫的完美用例是當所需的數據一致性很高,並且關係被很好地定義。一個電子商務平臺的報告系統,其中有包括客戶、訂單、付款和產品的表格,將是關係型數據庫的完美候選者。原因是關係被很好地定義,需要利用交易,並且需要使用關係來執行定製的查詢,以進行分析或報告。關係型數據庫的另一個不言而喻的好處是有大量的文件可用,這使得開發過程更加容易。
圖數據庫
例子。Amazon Neptune, Neo4j
圖形數據庫使用節點(定義存儲的數據)和邊(存儲節點之間的關係)來定義。這種關係的存儲意味着連接這些數據集是非常快的,相反,關係型數據庫會在查詢時計算這些數據,使整個過程變慢。
利用圖形數據庫的獨特功能需要一種優化的查詢語言,這導致了許多不同的查詢語言,如 Cypher、SparQL 和 Gremlin。這些查詢語言採用模式匹配來提取數據。例如,如果我想直接查詢 Rob 認識的人,一個查詢可以是這樣的。
MATCH (a:Person {name: "Rob"})-[:KNOWS]->(b)。
RETURN b
直觀地說,圖形數據庫非常適合於存儲事物之間的聯繫數據,例如社交網絡上的朋友、廣告顯示或欺詐預防。相反,它們不太適合存儲諸如交易歷史的東西。每個數據庫對 ACID 交易的支持和擴展能力各不相同,所以在選擇之前值得研究一下。
文檔數據庫
例子。MongoDB, CouchDB, ElasticSearch
文檔數據庫通常將數據存儲爲結構化的嵌套文檔(想想 JSON/BSON,XML),這意味着它們直觀地對應於你代碼中的對象。這些文檔被存儲在集合中,類似於關係數據庫中的行和表。
圖 3:一個文件的例子
文檔允許你使用靈活的模式來存儲半結構化的信息,允許你由於不斷變化的需求而輕鬆更新。在這方面,這是與關係型數據庫的主要區別之一,後者需要一個 SQL 語句來更新模式。這種靈活性使文檔數據庫非常適合於存儲任何可能有不同內容的東西,但你需要靈活的查詢;想想產品信息或客戶細節。
大多數文檔數據庫都以某種形式支持連接功能,然而,文檔的設計使用方式意味着你通常不應該這樣做。例如,在關係型數據庫中,一個客戶和客戶的聯繫方式通常會被存儲在兩個獨立的表中,但是在文檔數據庫中,你會利用分層結構將它們存儲在一個文檔中。
大多數文檔數據庫也支持 ACID 事務,並且通常支持水平擴展;這種擴展允許你支持巨大的讀取查詢量。MongoDB 還支持強一致性(線性化),這總是很方便。
鍵 / 值數據庫
例如。Redis, Berkeley DB
鍵 / 值存儲是概念上最簡單的數據庫;它是一種非關係型數據庫,其中的值是針對鍵來存儲的。這些值可以是簡單的單項數據,也可以是更復雜的對象,類似於文檔。這聽起來與文檔數據庫極爲相似,然而在鍵 / 值數據庫中,針對鍵存儲的信息不太透明。這意味着,對於文檔數據庫來說,你可以針對非主鍵進行查詢,允許更高的靈活性,而對於鍵 / 值數據庫來說,你通常只能針對一個主鍵進行查詢。
雖然鍵 / 值數據庫的靈活性較低,但這導致了更好的讀和寫性能,所以也有好處當你知道你要查詢什麼時,它們是最好用的。然後,找到一個已知的鍵的信息是非常快的,然而,如果你需要通過一個非主鍵過濾,這就變得更加困難。
鍵 / 值數據庫的使用情況與文檔數據庫非常相似;用戶檔案、會話數據或簡單的緩存數據。與文檔數據庫一樣,它們通常也支持橫向擴展。
寬列數據庫
例子。Apache Cassandra, Bigtable, ScyllaDB
在查詢大數據以生成分析和報告時,你很少希望查詢每一行的每一列,這樣做的效率相當低。即使減少所選列的數量,這仍然會導致大量不必要的數據被解析! 寬列 / 列族數據庫,通過在列中劃分數據來規避這個問題,當需要查詢時,只檢索所需的列。這樣做的結果是一個分區列的稀疏矩陣,包含一個單一的數據類型(寬列),或存儲一行的列族,而這一行又有嵌套的列和值在其內部。
圖 4:寬列 / 列族數據庫結構的例子
寬列數據庫對於諸如數據記錄、報告甚至消息服務都是非常棒的 --Discord 寫了一篇關於他們從 MongoDB 遷移到 Apache Cassandra 的精彩文章。他們的最佳用例是當你有可預測的查詢模式,高寫入和低讀取頻率的要求。稀疏矩陣設計允許在數據庫內有很高的靈活性;一行不一定要使用所有的列,這意味着不同的行可以由不同的列組成。
寬列數據庫通常以較低的線性化(由於水平縮放)來交換這種高寫入速度,這意味着當你查詢時,你不能保證獲得最新的寫入信息。
選擇數據庫時需要考慮的因素
CAP 和 ACID 中的 C 非常不同,不是特別重要。
我經常看到有兩個概念被混淆了,那就是 CAP 和 ACID 中的 C;雖然兩者在這些縮寫中都代表着一致性,但它們本質上是兩種完全不同的東西。
從 ACID 中的 C 開始,這裏的一致性意味着關於你的數據的某些約束或聲明必須始終被遵守;無論是這種類型、某些組合、限制還是唯一性。然而,這就是數據庫在一致性方面的能力限制 -- 任何更復雜的東西都不能由數據庫來處理。大多數數據庫默認提供這種級別的一致性,它不能替代由業務邏輯產生的不變性,隨後也不能阻止忽略這些的事務完成。因此,在我看來,原子性、隔離性和持久性是數據庫所提供的更重要的特性,也是你應該更重視的東西。
相反,CAP 中的 C(見這篇文章中 CAP 的缺陷),最好被稱爲線性化,它可以簡單地分解爲:當一個數據庫被寫入時,任何後續的讀取都會看到剛剛被寫入的確切數據。你可以想象,在某些應用中,這一點是至關重要的 -- 例如,在下訂單之前檢查客戶的餘額,或者產品的庫存水平。
圖 5:缺乏可線性化如何影響應用行爲的例子
很多數據庫儘管有複製,但可以提供線性化,雖然它不是即時的,但複製之間的延遲可以快到對你的應用沒有實際影響。線性化也不是一個數據庫的基本質量,通過犧牲它,你可以在其他方面獲得好處,比如說讀寫速度。不需要高線性度的例子可以是一個報告系統,它彙總了訂單量或有多少用戶喜歡一個社交媒體帖子 -- 任何不需要精確實時值的地方。
需要什麼樣的關係?
當查詢你的數據時,需要什麼樣的關係?在我看來,這是區別上述數據庫類型的最大因素之一;而不是 SQL 與 NOSQL。這個問題在很大程度上取決於你的應用程序的預期行爲,以及它所構建的架構。
在這方面沒有一個放之四海而皆準的數據庫,也不應該有人幻想使用單一的數據庫類型是唯一的方法;單獨的服務可能需要完全不同的數據庫。當首先決定你希望使用什麼數據庫時,對你的數據進行建模 -- 定義實體和關係,這很可能會清楚哪種數據庫最適合你。同樣相關的是,擁有關係並不是必須的,如果你能以一種避免這些關係的方式來構造你的數據,就能使你的應用程序的性能受益;關係總是有代價的。
要求 - 它們是衆所周知的嗎?
這個問題相當廣泛,可能意味着大量的先決條件,從功能性到非功能性。除了應用行爲之外,還有無數可能的要求;你是否被鎖定在一個雲供應商?你需要一個完全管理的服務嗎?安全性有多重要?所有這些東西都不會被認爲是前幾節中的主要要求,但是它們都在選擇數據庫的動態過程中起着很大的作用。
當你考慮在你的主機生態系統之外的選擇時,你會發現自己花在管理這些解決方案上的時間遠遠超過最初的預算 -- 安全更新、故障轉移和備份。當然,這些都是次要的,因爲數據庫的主要要求和他們給你的能力,但是必須考慮這些因素以避免進一步的頭痛。
結論。我們是如何駕馭這個仙境的?
這些考慮,再加上敏捷開發中對快速移動和快速迭代的渴望,意味着在開發過程的早期花費時間將得到回報 -- 在後期階段的遷移是昂貴的,最好是避免的。這個決定不僅會影響你的應用程序的性能,也會影響開發過程。
上面列出的因素並不是唯一需要考慮的因素,它們是我認爲經常被忽視的因素。我徹底建議花大量時間審查和測試儘可能多的選擇,以瞭解每個可用選擇之間的根本區別。
在我們自己經歷了這個過程之後,我們在鍵 / 值和文檔混合數據庫和文檔數據庫之間選擇了一個。我們把範圍縮小到 DynamoDB 和 DocumentDB 之間 --AWS 的一個 MongoDB 分叉。
首先,在 WeGift,我們目前的基礎設施都在 AWS 上;我們希望有一個管理服務來執行安全更新、備份和擴展。這兩點都支持強一致性的讀取,這也是我們作爲這個功能的一部分所需要的。
最後,模式的靈活性非常適合我們的要求;我們將從第三方供應商那裏存儲不同的元數據,而擁有良好的開發者體驗的願望在我們的清單上名列前茅;不必編寫模式遷移肯定有助於實現這一點。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/cAceFXxNTx3xY-E7sfG7HA