Mysql、MongoDB?如何選擇合適的數據庫

我們正在做一個電子書的小程序。

1.0 層次模型數據庫

用戶購買,生成訂單,訂單詳情裡有用戶購買的電子書:

Mysql、MongoDB?如何選擇合適的數據庫

一層一層鋪開,一對多,這是「層次模型數據庫」(Hierarchical Database)。

2.0 網狀模型數據庫

一筆訂單可以購買多本電子書,一本電子書也可以被多筆訂單購買:

Mysql、MongoDB?如何選擇合適的數據庫

這就形成了「多對多」的「網狀模型數據庫」(Network Database)。

上面講的兩種數據庫,也許你聽都沒聽過。

我們用的,是「關係模型」,而非上面的「層次模型」或者「網狀模型」。

為什麼?

你會說,這樣不方便遍歷所有訂單。

並不會,再加一個根節點就好:

Mysql、MongoDB?如何選擇合適的數據庫

你會說,這樣查找效率很低。

也不會,因為可以優化下數據結構,比如換成 B+ 樹。

為什麼我們從一開始就在用「關係模型數據庫」?

3.0 關係模型數據庫

無論是層次模型還是網狀模型,程序員看到的,都是實實在在的物理存儲結構。

查詢時,你要照著裡面的數據結構,用對應的算法來查;

插入時,你也要照著數據結構,用對應算法來插入,否則你就破壞了數據的組織結構,數據也就壞掉了。

因為我們都沒用過前面兩種數據庫,所以覺得「關係模型數據庫」(以下簡稱 RDB)的一切都理所當然,但其實,它做出了一個革命性的變革:

用邏輯結構(logical representation of data)代替物理結構(physical representation of data)

所謂「邏輯結構」,也就是我們經常看到的「表格」,User 是一張表格,Order 是一張表格,Book 又是一張表格,它們之間的關係,用 id 來關聯,這些 id,可能是 number 類型,也可能是 string 類型:

Mysql、MongoDB?如何選擇合適的數據庫

但你看到的,不一定就是實際的,你看到的只是讓你方便理解的「邏輯結構」,真實數據自然不是這樣按表格來存儲,表格無異於一個數組,數組查詢是很慢的。

真實的「物理結構」,也許還是像「層次模型」和「網狀模型」一樣,是複雜的數據結構。

但到底是怎樣的數據結構,你都無需關心,你只需把它想象成一張「表」去操作,就連可視化工具,都會幫你把數據可視化成表,來方便你理解。

這個觀念的提出,來自於 1970 年 Codd 的一篇論文,A Relational Model of Data for Large Shared Data Banks:

Future users of large data banks must be protected from having to know how the data is organized in the machine (the internal representation).

Activities of users at terminals and most application programs should remain unaffected when the internal representation of data is changed and even when some aspects of the external representation are changed.

—— Codd

Codd 的這種思想,其實就是經濟學裡提到的:分工產生效能。

程序員們不需要直接和物理結構打交道,只負責告訴數據庫,他想做什麼,至於數據是如何存儲、如何索引,都交給數據庫,最終他們看到的就是一張張特別直觀、特別好理解的 excel 表格。

而數據庫則把維護物理結構的複雜邏輯,交給了自己, 對程序員屏蔽了複雜的實現細節。

開發時寫的代碼少了,耦合性降低了,數據也不容易損壞,也就提高了生產效率(productive)。

一切能用同樣的耗能,帶來更多效能的技術,都會被廣泛使用。

NoSQL

那後來為什麼又有了 NoSQL 呢?

在 RDB 被髮明的時代,軟件多用於大型企業,比如銀行、金融等等,人們對數據的要求非常純粹:準確、可靠、安全,讓數據按照期望,正確的寫入,不要給老子算錯錢就好,於是有了具有 ACID 特性的事務:原子性、一致性、隔離性和持久性。

那時候用網絡的人很少,通過終端來訪問客戶端的人,更少,自然的,數據庫的數據量和訪問量都跟現在沒法比,一臺機器,足矣,最多再來個一主多從:

Mysql、MongoDB?如何選擇合適的數據庫

後來,你知道的,每個人手裡都有個手機,每分每秒,都有成千上萬的數據,寫入你的數據庫、從你的數據庫被查出,於是有了「分佈式」,有了 BASE 和 CAP。這時候,RDB 就會發現,自己之前的那一套 ACID,竟然有點作繭自縛了:

  • 為了保證事務的隔離性,要進行加鎖,在分佈式的環境下,就要對多臺機器的數據進行加鎖;
  • 為了保證事務的原子性,在機器 A 的操作和在機器 B 的操作,要麼一起成功,要麼一起失敗;
  • ……

這些都要去不同節點的機器進行通訊和協調,實現起來非常複雜,而且要付出更多的網絡 IO,影響性能。

ACID 在分佈式系統上實現起來就會變得難以實現,即使實現了,也要付出很大的性能成本,於是才有了後來的各種「分佈式一致性協議」,Paxos、Raft、2PC …… 而 Mysql 也提供了各種方案來實現分佈式,當然,這些方案自然是很複雜的,比如 「NDB Cluster」 :

Mysql、MongoDB?如何選擇合適的數據庫

而 NoSQL 則沒有這麼多承諾,它的一致性,一般都是最終一致性,當然你可以選擇強一致,那自然就要付出點性能作為代價,當然你還可以弱一致,這樣會更不安全,但是更快,一切取決於你對數據的要求。

除此之外,RDB 的「數據庫範式」(Database Schema),也成了限制擴展性的瓶頸。為了避免數據冗餘導致的各種問題(佔用空間、刪除異常、更新異常等等),我們在設計關係模型時,通常都是按照最小單位來設計的。

什麼叫最小單位,比如用戶有地址和愛好,那麼在正確設計的關係模型(比如 3NF)裡,這就是三張表:

Mysql、MongoDB?如何選擇合適的數據庫

如果這三張表被分散在不同的機器,那進行關聯查詢時,就需要多次跨機器的通訊;

而對於 NoSQL,這三類信息,都可以利用 Json 格式的數據,將它們存放在一起:

Mysql、MongoDB?如何選擇合適的數據庫

完整的存儲進去,完整的取出來,不需要額外的操作。

NoSQL 比 RDB 有更強的擴展性,可以充分利用分佈式系統來提升讀寫性能和可靠性。

這不是誰設計好壞的問題,而是跟他們要解決的問題有關:RDB 誕生於互聯網萌芽的時代,那時數據的準確、可靠是最重要的,而 NoSQL 誕生於互聯網快速發展普及的時代,大數據、分佈式、擴展性成了數據庫的另一個重要特性。

總結一下:

  • RDB 首先得是準確、可靠,然後才向更高的「可拓展性」發展;
  • 而 NoSQL 生而分佈式,可拓展性強,然後才向更高的「準確性」發展。

NoSQL ,not only SQL,其實就是對那種打破了 RDB 嚴格事務和關係模型約束的那些數據庫的泛指,而隨著要解決的問題的不同,又誕生了各種各樣的 NoSQL。

首先是「列式數據庫」(Column-oriented DBMS),數據量上去了,我們想分析網站用戶的年齡分佈,簡單說,就是你需要對同一個特徵進行大數據量的分析統計,於是把原來 RDB 的「按行存儲」的範式打破,變成了「按列存儲」,比如 HBase;

然後你發現有些數據變動不是很大,但是經常需要被查詢, 查詢時還要關聯很多張表,於是你把這些來自不同表的數據,揉成一個大對象,按 key-value 的格式存起來,比如 Redis;

再後來你需要對博客內容進行相關性搜索,傳統 RDB 不支持相關性搜索,最重要的,還是擴展性差,增加機器的帶來邊際效益有限,於是有了「全文搜索引擎」,比如 Elasticsearch;

除此之外,還有「文檔數據庫」、「圖形數據庫」……

沒有一種數據庫是銀彈。

總結

這篇文章的題目是「如何選擇數據庫」,這是困擾很多人的問題,那麼多數據庫,到底要選什麼好?

可是當你問出這樣一個問題時,其實你是在問一種「手段」。我現在要做這樣一個需求,用什麼數據庫可以幫我實現它?

但其實你需要的不只是一種「手段」,因為如果對方甩給你一個冷冰冰的名字,Mysql、Elasticsearch、MongoDB,你肯定會問,憑什麼?

你需要的,是一種「解決方案」。如果你需要數據十分嚴格準確,分毫不差,那我會推薦你採用「事務」和「關係模型」來處理數據;如果你需要數據能夠被大量讀取和寫入,那我會推薦你擴展性強的「分佈式」;如果你的數據經常是整個讀取、整個更新的,那「關係模型」就沒有「文檔模型」適合你。

「事務」、「關係模型」、「分佈式」、「文檔模型」等等,這些就是「解決方案」,知道用什麼「解決方案」,用哪個數據庫,自然水到渠成。

正如一位大牛說的:

設計實踐中,要基於需求、業務驅動架構。無論選用 RDB/NoSQL,一定是以需求為導向,最終數據存儲方案必然是各種權衡的綜合性設計。

用戶不會因為你用了 Mysql 或者 MongoDB 而使用你的軟件,畢竟絕大多數用戶都不知道 Mysql 和 MongoDB 是什麼玩意。


分享到:


相關文章: