資料庫,你真的用對了嗎?—— SQL vs. NoSQL

AppDev Ooops
AppDev Ooops
Published in
8 min readApr 10, 2023

這篇文章想跟你分享

「這個專案到底該用 MySQL 還是 MongoDB 呢?」,相信這個問題,應該經常在專案建立之時出現,最後到底會選擇使用哪一種資料庫系統?亦或甚至混合使用?在進行決策前,是否發現只了解資料庫怎麼「用」,但在不同情境下建置方案,應具備哪些知識呢?有哪些該評估及納入考量的特性呢?本篇文章簡單整理 SQL 與 NoSQL 的優劣勢,並且比較常見的資料庫系統。

RDBMS vs. NoSQL

「應該就是 ⋯⋯ MySQL vs. MongoDB 吧」

資料庫系統不論在哪種量級的資料量,都必須保證一定水準的資料處理速度。讀取 / 寫入資料時,需要先透過「定位」找出目標資料再進行動作,而資料處理消耗的時間會隨資料量呈正比上升,因此必然有其他的機制來輔助「定位」。SQL 與 NoSQL 的 Index Mechanism 設計上存在差異,在比較與探討兩種資料庫系統時需要多加留意。

關聯式資料庫 RDBMS

RDBMS(Relational Database Management System)使用 Data Tables 儲存相關聯的資料,並以資料表行與列的的二元關係呈現。格式設計嚴格,同一張資料表欄位紀錄相同的資料與資料格式。雖然每個 Transaction 可能包含多個更新動作,但因為都是 Atomic Action,因此符合 ACID 原則

在寫入資料前,需預先設計並實作 Schema 之各項定義,包含:Primary Keys、Indexes、Relationships、Triggers、Stored Procedures⋯⋯等。

傾向採用 Normalization 的方式來儲存資料,透過參照到另一張紀錄資料 Index 的表格減少冗餘資料,並在接受查詢時,透過 JOIN 的語法,取得多張 表格的相關資料,完成一個 Query 內的工作。

「又快速又符合 ACID 原則,RDBMS 好棒棒?」

關聯式資料庫有個致命的缺點,那就是「難以實現橫向擴展」,雖縱向擴展則可透過 Clustering 方式達成,然而擴展空間較有限。

基於 B-Tree 資料結構的 B-Tree Index 作為索引方式,建立索引表。在 B-Tree Index 中,Leaf Node 被稱為 Leaf Page,完整的 Index 資訊只儲存此,且每一棵 B-Tree 的 Leaf Node 深度皆相同;而 Node 則被稱為 Directory Page,只儲存 Key,可見 Key 的設計對資料庫查找效率表現至關重要。

在執行 Query 時,不管是查詢、新增或刪除,皆需要循著 Key 下探到 Leaf Page,才能取得資料實際位置。而在新增資料時,因 B-Tree 高度不允許擴充的特性,須在 Leaf Node 上額外加掛 Overflow Block。不過,大致上來說,B-Tree Index 除可進行範圍搜尋外,Equality Search & Update 和 Range Search 的時間複雜度皆有良好的表現。

🔍 深入瞭解 B-Tree 資料結構:

非關聯式資料庫 NoSQL

NoSQL 指的是 Not Only SQL,由於「不是用 SQL 操作資料的資料庫設計」,並不是取代 SQL 的新技術,因此實際上資料之間還是可建立關聯。

NoSQL(Not Only SQL)的儲存資料的方式相當多元,最常見的為類似 JSON,以欄位與數值成對(Field-Value Pair)的 Document 儲存資料,其他尚有:Key-Value Cache (e.g. Memcached, Redis)、Key-Value Store (e.g. Oracle NoSQL Database, Redis)、Document Store (e.g. MongoDB, Elasticsearch)、Wide Column Store (e.g. Amazon DynamoDB)、Graph (e.g. ArangoDB, OrientDB )。

自行掌握資料結構

類似的 Documents 會儲存在同一個 Collection 中,而 Collection 則是類似 SQL 的表格。不過 NoSQL 格式設計較不嚴謹,可以儲存任何形式的資料,且不需要事先設計或定義好 Document 或 Collection,隨時間演進而需要增加資料種類時,可直接將某欄位資料之格式設為 JSON。雖然更新單一 Document 內一個以上的值,都算是 Atomic Action,但在多個 Documents 間並沒有等同於 Transaction 的操作。

解決了歷史變更問題

通常採用 Denormalization 的方式來儲存資料,因此即便無支援 JOIN 語法,依舊不用進行複雜的 SQL Query,便可在單次請求內取回所需的相關資訊,減少 query 次數,所以普遍皆認為「NoSQL 的速度較 SQL 更快」,不過,在更新多筆數資料時就會明顯較慢。

應用於電商系統更會凸顯 NoSQL 的優點,在每筆訂單都重複儲存用戶基本資訊,雖空間成本提高,卻能有效解決歷史變更的問題。舉例來說,若用戶成功建立了一筆訂單,卻在之後更改了用戶資訊中的地址,此時原訂單資料中的地址便不會受到影響而跟著改變。

易於橫向擴展

另一個優點,便是其易於橫向擴展特性,歸功於其採用的 Hash-Based Index 索引機制。透過 Hash Function, Key 轉換成一組獨特的 Hash Value,而 Hash Value 對 Index 的 Mapping 便記錄於 Hash Table 中。

雖然 Hash-Based Index 運作非常快速,但 Hash Function 不會知道數值範圍的上界或下界,因此無法進行範圍性的索引,例如:tc > 1680088796,還會有 Overflow 的情形存在,也就是將不同 Key 重複儲存於相同位置。此外,採用不同的 Hash-Based Index 設計也對資料庫系統之特性有所影響。

🔍 深入瞭解不同的 Hash-Based Index 設計

適用情況

SQL 適用情況:

  1. 資料格式明確,未來不會大幅的變動
  2. 資料之間的關聯很重要
  3. 想要更有效率的讀取資料
  4. 未來會大量使用到 JOIN 的功能
  5. 更著重在資料操作的準確性與一致性 (ACID)

NoSQL 適用情況:

  1. 想快速啟動小專案進行 POC
  2. 資料格式未來很有可能調整
  3. 資料之間沒有複雜的關聯、跨區統計需求較少
  4. 大用量的查找需求

MongoDB vs. AWS DynamoDB

以下針對較常見的兩個 NoSQL 資料庫進行比較,最主要的差異點在於處理數據結構的方式。

MongoDB

  • 開源資料庫,可以在任何地方運行,不需依賴於任一生態系,且有免費的開源版本
  • 是文件資料庫,透過文檔資料模型,可以映射到應用程序代碼中的對象,從而可以輕鬆處理數據
  • 每筆資料為一個 Document,可對應到傳統資料庫的 Row,透過 Collection 集中這些 Documents,可對應到傳統資料庫的 Table
  • 沒有固定的模式來定義如何存儲數據,容納多元且大量的數據類型儲存,例如:常規 JSON 和高級 BSON 數據模型,包含 int、long、date、timestamp、geospatial、floating-point⋯⋯等
  • 可以垂直和水平擴展
  • 不支持高性能JOIN 而需要進行非規範化,而是使用屬於一起的數據存儲在一起的理念來完全避免使用JOIN。此限制可能導致數據大小和相關成本攀升。
  • 每次有人在集合中插入新文檔時,索引都必須更新
  • 可以實現多樣化的資料模型,例如 Key/Value,Sub Documents 等方式

AWS DynamoDB

  • 由 Amazon 開發,只能在 AWS 生態系統內使用
  • 採鍵值存儲,表、屬性和項目是必須使用的主要組件,每個查詢都必須提供一個分區鍵,並且可以選擇為排序鍵指定單個值或範圍
  • 可按需水平擴展,以支持幾乎無限的讀寫操作,有利於大規模性能,但也會反映在成本上
  • 通過鍵查詢時,查詢性能不會隨著數據庫大小而降低,一個 partition 同時間可以支援 3000 RPS,用 request router 做控制,響應時間低於個位數毫秒,但 DynamoDB 索引有限且複雜
  • 使用 AWS 雲原生的架構去備份,達到高可用性、分散風險,每個 partition 都有三個可用區(AZ, Available Zone),其中一個是 leader,每個 AZ 都有兩個 Response,只要六份中有四份寫入成功,就會回傳「寫入成功」
  • 適合移動、網絡、遊戲和廣告技術

小結

千萬別被「聽說那個新出的工具很好用」給蒙蔽了雙眼,最重要的問題應該在於「問題能被解決嗎?」。身為工程師,我們應該深入了解每個工具的特性之後尋找最適合的解法,否則雖然專案可以運作了,但無形之中可能犧牲了許多效率與成本。你也有用過 SQL 與 NoSQL 嗎?或者你也對資料庫的使用有其他想法歡迎文章內留言,或者到 AppDev Ooops 粉絲專頁留言喔!

--

--