從SQL到NoSQL,數據庫還要向何處演進?
在開發一個應用程序時,不可避免要選擇使用SQL還是NoSQL數據庫來存儲數據。傳統的數據庫,即使用SQL(結構化查詢語言)進行查詢的關系型數據庫,是經過幾十年來技術發展、良好實踐和現實世界壓力測試的產物。它們是為可靠的事務和臨時查詢而設計的,是業務線應用的主力軍。但它們也有一些限制,如嚴格的模式,使它們不太適合于其他類型的應用。
NoSQL數據庫是針對這些限制而產生的。NoSQL系統存儲和管理數據的方式,允許高操作速度,并讓開發人員有巨大靈活性。許多數據庫是由谷歌、亞馬遜、雅虎和Facebook等公司開發的,它們尋求更好的方式來存儲內容或處理大型網站的數據。與SQL數據庫不同,許多NoSQL數據庫可以在數百或數千臺服務器上進行橫向擴展。
不過,NoSQL的優勢并不是沒有代價的。NoSQL系統傾向于速度和可擴展性,而不是SQL數據庫所承諾的可靠事務背后的ACID屬性。與圍繞SQL建立的數十年的機構知識相比,在NoSQL系統中用于處理數據的隱喻也是相對較新的。
SQL和NoSQL數據庫提供了不同的權衡。雖然它們在特定項目的背景下可能會有競爭,例如,為不同應用會面臨二選一的情況,但它們在更大范圍內是互補的,每一種都適合于不同的使用情況。是選擇SQL還是NoSQL并不是絕對的,要根據場景需求選合適的。
NoSQL與SQL
SQL和NoSQL之間的根本區別并不那么復雜。對于如何存儲和檢索數據,兩者都有不同的理念。
對于SQL數據庫,所有數據都有一個固有的結構。像Microsoft SQL Server、MySQL、PostgreSQL或Oracle數據庫這樣的傳統數據庫使用一個模式--對插入數據庫的數據如何組成的正式定義。例如,一個表中的某一列可能被限制為只能是整數。因此,記錄在該列中的數據將有很高的規范化程度。SQL數據庫的嚴格模式也使得對數據進行聚合變得相對容易,例如,使用SQL JOIN命令將兩個表的數據合并。
在NoSQL中,數據可以以無模式或自由的方式存儲。任何數據都可以存儲在任何記錄中。在NoSQL數據庫中,你會發現四種常見的數據存儲模式,這導致了四種常見的NoSQL系統類型。
- 文檔數據庫(如MongoDB)。插入的數據以無模式的JSON結構或“文檔”的形式存儲,其中的數據可以是任何東西,從整數到字符串再到自由格式的文本。沒有必要指定JSON文檔將包含哪些字段(如果有的話)。
- 鍵值存儲(如Redis)。自由格式的值,從簡單的整數或字符串到復雜的JSON文檔,都可以通過鍵(比如字符串)在數據庫中訪問。
- 寬列存儲(如 Cassandra)。數據被存儲在列中,而不是像傳統SQL系統存儲在行。任何數量的列(以及許多不同類型的數據)都可以根據查詢或數據視圖的需要進行分組或聚合。
- 圖數據庫(如Neo4j)。數據被表示為實體及其關系的網絡或圖形,其中圖中的每個節點是一個自由形式的數據塊。
無模式的數據存儲在以下情況下是有用的。
- 你想快速訪問數據,你更關心訪問的速度和簡單性,而不是可靠的事務或一致性。
- 你正在存儲大量的數據,你不想把自己鎖定在一個模式中,因為以后改變模式可能是緩慢和痛苦的。
- 你正在從一個或多個來源接收非結構化數據,你想保持數據的原始格式以獲得最大的靈活性。
- 你想把數據存儲在一個分層結構中,但你希望這些分層結構由數據本身來描述,而不是外部模式。NoSQL允許數據隨意地自我引用,該方式對于SQL數據庫來說更為復雜,難以模仿。
查詢NoSQL數據庫
關系型數據庫使用的結構化查詢語言提供了一種統一的方式,在存儲和檢索數據時與服務器通信。SQL語法是高度標準化的,所以盡管各個數據庫可能會以不同的方式處理某些操作(例如,窗口函數),但基本原理仍然是相同的。
相比之下,每個NoSQL數據庫往往都有自己的語法來查詢和管理數據。例如,CouchDB使用JSON形式的請求,通過HTTP發送,以創建或檢索其數據庫中的文檔。MongoDB通過二進制協議,以命令行接口或語言庫的方式發送JSON對象。
一些NoSQL產品可以使用類似SQL的語法來處理數據,但只是在有限的范圍內。例如,Apache Cassandra,一個廣泛的列存儲,有自己的類似SQL的語言,Cassandra查詢語言(CQL)。CQL的一些語法是直接來自于SQL的手冊,比如SELECT或INSERT關鍵字。但在Cassandra中沒有執行JOIN或子查詢的本地方法,因此相關的關鍵字在CQL中并不存在。
無共享shared-nothing架構
NoSQL系統常見的一個設計選擇是“shared-nothing”架構。在一個無共享的設計中,集群中的每個服務器節點都獨立于其他節點運行。系統不需要從其他節點獲得共識來返回數據給客戶端。查詢速度很快,因為它們可以從最近的或最方便的節點返回。
無共享系統的另一個優點是彈性和向外擴展。向外擴展集群非常容易,只需旋轉集群中的新節點并等待它們與其他節點同步即可。如果一個NoSQL節點宕機,集群中的其他服務器將繼續運行。即使服務請求的節點減少,所有數據仍然可用。
請注意,無共享的設計并不是NoSQL數據庫所獨有的。許多傳統的SQL系統可以以無共享的方式設置,如MySQL,盡管這通常涉及到犧牲整個集群的一致性以獲得性能。
NoSQL的局限性
如果NoSQL提供了如此多的自由和靈活性,為什么不完全放棄SQL?答案很簡單,許多應用仍然需要SQL數據庫所提供的各種約束、一致性和保障措施。在這些情況下,NoSQL的一些“優勢”可能會變成劣勢。其他的限制來自于NoSQL系統缺乏某些在SQL領域中本應有的功能。
(1) 無模式(No schema)
即使你接收的是自由格式的數據,你也幾乎總是需要對數據施加約束,以使其有用。對于NoSQL,施加約束涉及到將責任從數據庫轉移到應用開發者身上。例如,開發者可以通過一個對象關系映射系統(或稱ORM)強加結構。但如果你想讓模式與數據本身共存,NoSQL通常不支持這種做法。
一些NoSQL解決方案為數據提供了可選的數據類型和驗證機制。例如,Apache Cassandra有一系列的本地數據類型,讓人想起傳統SQL中的那些數據類型。
(2) 最終一致性
NoSQL系統提供了強一致性或即時一致性的選擇,以獲得更好的可用性和性能。傳統的數據庫確保操作是原子的(事務的所有部分都成功,或者沒有一個成功)、一致的(所有用戶都有相同的數據視圖)、隔離的(事務不競爭)和持久的(一旦完成,它們將不受服務器故障的影響)。
這四個屬性,統稱為ACID,在NoSQL系統中可以用不同的方式處理。你可以選擇最終一致性,而不是要求整個集群的強一致性,這必然會延遲對請求的響應,允許服務請求,而無需等待最新的寫入復制到集群的其它節點。插入集群的數據最終在各處都是可用的,但不能保證任何時候可用。
對于一些NoSQL系統,你可以在一致性和速度之間選擇一個折中方案,不同的產品有不同的方案。例如,微軟的Azure Cosmos DB可以讓你選擇每個請求的一致性級別,所以你可以選擇適合你的。事務語義,在SQL系統中保證事務中的所有步驟(例如執行銷售和減少庫存)要么完成,要么回滾,在一些NoSQL系統中也有,例如MongoDB。
(3) NoSQL的鎖定
大多數NoSQL系統在概念上是相似的,但實現方式不同。每個系統都有自己的隱喻和機制,用于數據的查詢和管理。
這樣做的一個副作用是應用邏輯和數據庫之間可能存在高度的耦合。如果你選擇一個NoSQL系統并堅持使用它,這種耦合性并沒有什么壞處,但如果你在未來更換系統,它就會成為一個絆腳石。
如果你從MongoDB遷移到CouchDB(或者相反),你需要做的不僅僅是遷移數據。還必須駕馭數據訪問和編程隱喻之間的差異。換句話說,你必須重寫應用程序中訪問數據庫的部分。
(4) NoSQL技能
NoSQL的另一個缺點是相對缺乏專業知識。傳統SQL人才的市場相當大,而NoSQL技能的市場卻剛剛起步。
作為參考,Indeed.com報告稱,截至2022年,傳統SQL數據庫(MySQL、微軟SQL Server、Oracle數據庫等)的職位數量仍然高于MongoDB、Couchbase和Cassandra的職位數量。對NoSQL專業知識的需求仍然只是SQL技能市場的一小部分。
合并SQL和NoSQL
未來,隨著時間的推移,SQL和NoSQL系統之間的一些差異會消失。現在已經有許多SQL數據庫接受JSON文檔作為原生數據類型,并可以對該數據進行查詢。一些數據庫甚至有對JSON數據施加約束的本地方法,因此其處理方式與傳統的行和列數據一樣嚴格。
另一方面,NoSQL數據庫不僅增加了類似SQL的查詢語言,還增加了傳統SQL數據庫的其他功能,比如MongoDB的ACID屬性。
一個可能的路徑是,未來幾代數據庫以及當前數據庫系統的未來版本將跨越這些范式,同時提供SQL和NoSQL功能,有助于使數據庫世界不再支離破碎。例如,微軟的Azure Cosmos DB在底層使用了一套基元,可以互換地再現兩種系統的行為。谷歌云Spanner將SQL的強一致性與NoSQL系統的水平可擴展性相結合。
不過,純SQL和純NoSQL系統仍將在未來很多年占有一席之地。在設計靈活性、水平擴展性和高可用性比強讀一致性和其他SQL數據庫常見的保障措施更重要的情況下,可以考慮使用NoSQL。對于許多應用來說,這些保障措施很可能值得用來交換NoSQL所提供的東西。
對于許多應用程序來說,以NoSQL的特有優勢來那些換保障措施是值得。