關系數據庫(Relational Database)是建立在關系模型基礎上的數據庫,借助于幾何代數等數學概念和方法來處理數據庫中的數據。所謂關系模型是一對一、一對多或者多對多等關系,常見的關系型數據庫有 Oracle、SQL Server、DB2、MySQL 等。
而文檔型數據庫是一種非關系型數據庫,非關系型數據庫(Not Only SQL,NoSQL)正好與關系型數據庫相反,它不是建立在“關系模型”上的數據庫。文檔型數據庫的典型代表是 MongoDB。
我們本課時的面試題是,關系型數據庫和文檔型數據庫有什么區別?
#### 典型回答
關系型數據庫屬于早期的傳統型數據庫,它有著標準化的數據模型,以及事務和持久化的支持、例如,關系型數據庫都會支持的 ACID 特性,也就是原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)和持久性(Durability),具體含義如下。
* 原子性(Atomicity):是指一個事務中的所有操作,要么全部完成、要么全部不完成,不會存在中間的狀態。也就是說事務在正常的情況下會執行完成;異常的情況下,比如在執行的過程中如果出現問題,會回滾成最初的狀態,而非中間狀態。
* 一致性(Consistency):是指事務從開始執行到結束執行之間的中間狀態不會被其他事務看到。
* 隔離性(Isolation):是指數據庫允許多個事務同時對數據進行讀寫或修改的能力,并且整個過程對各個事務來說是相互隔離的。
* 持久性(Durability):是指每次事務提交之后都不會丟失。
關系型數據庫一般遵循三范式設計思想,具體內容如下。
第一范式(The First Normal Form,1NF):要求對屬性的原子性,也就是說要求數據庫中的字段需要具備原子性,不能再被拆分。
比如,用戶表中有字段:用戶 ID、用戶名、電話;而其中電話又可以分為:家庭電話和移動電話等。因此,此表不符合第一范式,如下圖所示:

第二范式(The Second Normal Form,2NF):例如訂單詳情表有這些字段:訂單 ID、產品 ID、產品名稱、產品單價、折扣。其中,訂單 ID 和產品 ID 為聯合主鍵,但這個表中的產品名稱和產品單價兩個字段只依賴產品 ID,和訂單 ID 就沒有任何關系了,因此這個表也不符合第二范式。
我們可以把原來的訂單表拆分為訂單表和產品表,其中訂單表包含:訂單 ID、產品 ID、折扣等字段;而產品表包含:產品 ID、產品名稱、產品單價等字段。這樣就消除了產品名稱和產品單價多次重復出現的情況了,從而避免了冗余數據的產生。

第三范式(The Third Normal Form,3NF):想要滿足第三范式必須先滿足第二范式,第三范式要求所有的非主鍵字段必須直接依賴主鍵,且不存在傳遞依賴的情況。
例如,有一個學生表中包含了:學生 ID、姓名、所在學院 ID、學院電話、學院地址等字段。這個表的所有字段(除去主鍵字段)都完全依賴唯一的主鍵字段(學生 ID),所以符合第二范式。但它存在一個問題,學院電話、學院地址依賴非主鍵字段學院 ID,而不是直接依賴于主鍵,它是通過傳遞才依賴于主鍵,所以不符合第三范式。
我們可以把學生表分為兩張表,一張是學生表包含了:學生 ID、姓名、所在學院 ID 等字段;另一張為學院表包含了:學院 ID、學院電話、學院地址等字段,這樣就滿足第三范式的要求了。

可以看出,使用三范式可以避免數據的冗余,而且在更新表操作時,只需要更新單張表就可以了。
但隨著互聯網應用的快速發展,我們需要應對日益復雜且快速迭代的數據庫,以應對互聯網快速發展的趨勢,于是誕生了以 MongoDB 為代表的文檔型數據庫。它提供了更高效的讀/寫性能以及可自動容災的數據庫集群,還有靈活的數據庫結構,從而給系統的數據庫存儲帶來了更多可能 性。
當然 MongoDB 的誕生并不是為了替代關系型數據庫,而是為系統的快速開發提供一種可能性,它和關系型數據庫是一種互補的關系,可供開發者在不同的業務場景下選擇相對應的數據庫類型。
#### 考點分析
本課時的面試題考察的是面試者對數據庫整體概念的理解與區分,這個問題看似簡單,但包含著眾多小的知識點,面試者需要真正的理解關系型數據庫和非關系型數據庫以及文檔型數據庫之間的區別才能靈活應對。與之相關的面試題還有:
*
非關系型數據庫和文檔型數據庫有什么區別?
* MongoDB 支持事務嗎?
#### 知識擴展
* [ ] 非關系型數據庫 VS 文檔型數據庫
非關系型數據和文檔型數據庫屬于包含關系,非關系型數據包含了文檔型數據庫,文檔型數據庫屬于非關系型數據。
非關系型數據通常包含 3 種數據庫類型:文檔型數據庫、鍵值型數據庫和全文搜索型數據庫,下面分別來看每種類型的具體用途。
* 1. 文檔型數據庫
文檔型數據庫以 MongoDB 和 Apache CouchDB 為代表,文檔型數據庫通常以 JSON 或者 XML 為格式進行數據存儲。
以 MongoDB 為例,它是由 C++ 編寫的一種面向文檔的數據庫管理系統,在 2007 年 10 月 由 10gen 團隊所開發,并在 2009 年 2 月首度推出。MongoDB 是以二進制 JSON 格式存儲數據的,MongoDB 對 JSON 做了一些優化,它支持了更多的數據類型,這種二進制存儲的 JSON 我們也可以稱之為 BSON(Binary JSON)。
BSON 具備三個特點:輕量、可遍歷以及高效,它的缺點是空間利用率不是很理想。MongoDB 使用 BSON 進行存儲的另一個重要原因是 BSON 具備可遍歷性。
MongoDB 存儲結構示例如下:
```
{"_id":ObjectId(“57ce2d4cce8685a6fd9df3a3"),"name":"老王","email":['java@qq.com','java@163.com']}
```
其中,“_id”為 MongoDB 默認的主鍵字段,它會為我們生成一起全局唯一的 id 值,并且這個值在做數據分片時非常有用。
文檔型數據庫的使用場景如下。
* 敏捷開發,因為 MongoDB 擁有比關系型數據庫更快的開發速度,因此很多敏捷開發組織,包括紐約時報等都采用了 MongoDB 數據庫。使用它可以有效地避免在增加和修改數據庫帶來的溝通成本,以及維護和創建數據庫模型成本,使用 MongoDB 只需要在程序層面嚴格把關就行,程序提交的數據結構可以直接更新到數據庫中,并不需要繁雜的設計數據庫模型再生成修改語句等過程。
* 日志系統,使用 MongoDB 數據庫非常適合存儲日志,日志對應到數據庫中就是很多個文件,而 MongoDB 更擅長存儲和查詢文檔,它提供了更簡單的存儲和更方便的查詢功能。
* 社交系統,使用 MongoDB 可以很方便的存儲用戶的位置信息,可以方便的實現查詢附近的人以及附近的地點等功能。
* 2. 鍵值型數據庫
鍵值數據庫也就是 Key-Value 數據庫,它的典型代表數據庫是 Redis 和 Memcached,而它們通常被當做非持久化的內存型數據庫緩存來使用。當然 Redis 數據庫是具備可持久化得能力的,但是開啟持久化會降低系統的運行效率,因此在使用時需要根據實際的情況,選擇開啟或者關閉持久化的功能。
鍵值型數據庫以極高的性能著稱,且除了 Key-Value 字符串類型之外,還包含一些其他的數據類型。以 Redis 為例,它提供了字符串類型(String)、列表類型(List)、哈希表類型(Hash)、集合類型(Set)、有序集合類型(ZSet)等五種最常用的基礎數據類型,還有管道類型(Pipeline)、地理位置類型(GEO)、基數統計類型(HyperLogLog)和流類型(Stream),并且還提供了消息隊列的功能。
此數據庫的優點是性能比較高,缺點是對事務的支持不是很好。
* 3. 全文搜索型數據庫
傳統的關系型數據庫主要是依賴索引來實現快速查詢功能的,而在全文搜索的業務下,索引很難滿足查詢的需求。因為全文搜索需要支持模糊匹配的,當數據量比較大的情況下,傳遞的關系型數據庫的查詢效率是非常低的;另一個原因是全文搜索需要支持多條件隨意組合排序,如果要通過索引來實現的話,則需要創建大量的索引,而傳統型數據庫也很難實現,因此需要專門全文搜索引擎和相關的數據庫才能實現此功能。
全文搜索型數據庫以 ElasticSearch 和 Solr 為代表,它們的出現解決了關系型數據庫全文搜索功能較弱的問題。
MongoDB 事務
MongoDB 在 4.0 之前是不支持事務的,不支持的原因也很簡單,因為文檔型數據庫和傳統的關系型數據庫不一樣,不需要滿足三范式。文檔型數據庫之所以性能比較高的另一個主要原因,就是使用文檔型數據庫不用進行多表關聯性查詢,因為文檔型數據庫會把相關的信息存放到一張表中。因此,無需關聯多表查詢的 MongoDB,在這種情況下的查詢性能是比較高的。
把所有相關的數據都放入一個表中,這也是 MongoDB 之前很長一段時間內不支持事務的原因,它可以保證單表操作的原子性,一條記錄要么成功插入,要么插入失敗,不會存在插入了一半的數據。因此,在這種設計思路下,MongoDB 官方認為“事務功能”的實現沒有那么緊迫。
但在 MongoDB 4.0 之中正式添加了事務的功能,并且在 MongoDB 4.2 中實現了分布式事務的功能,至此 MongoDB 開啟了支持事務之旅。
#### 小結
本課時我們首先講了關系型數據庫的 ACID 特性以及設計時需要遵循的三范式設計思想;然后介紹了以 MongoDB 為代表的文檔型數據庫與關系型數據庫的不同;最后還講了 MongoDB 的事務功能,以及文檔性數據庫與非關系型數據庫的關系,希望本課時的內容對你有幫助。
- 前言
- 開篇詞
- 開篇詞:大廠技術面試“潛規則”
- 模塊一:Java 基礎
- 第01講:String 的特點是什么?它有哪些重要的方法?
- 第02講:HashMap 底層實現原理是什么?JDK8 做了哪些優化?
- 第03講:線程的狀態有哪些?它是如何工作的?
- 第04講:詳解 ThreadPoolExecutor 的參數含義及源碼執行流程?
- 第05講:synchronized 和 ReentrantLock 的實現原理是什么?它們有什么區別?
- 第06講:談談你對鎖的理解?如何手動模擬一個死鎖?
- 第07講:深克隆和淺克隆有什么區別?它的實現方式有哪些?
- 第08講:動態代理是如何實現的?JDK Proxy 和 CGLib 有什么區別?
- 第09講:如何實現本地緩存和分布式緩存?
- 第10講:如何手寫一個消息隊列和延遲消息隊列?
- 模塊二:熱門框架
- 第11講:底層源碼分析 Spring 的核心功能和執行流程?(上)
- 第12講:底層源碼分析 Spring 的核心功能和執行流程?(下)
- 第13講:MyBatis 使用了哪些設計模式?在源碼中是如何體現的?
- 第14講:SpringBoot 有哪些優點?它和 Spring 有什么區別?
- 第15講:MQ 有什么作用?你都用過哪些 MQ 中間件?
- 模塊三:數據庫相關
- 第16講:MySQL 的運行機制是什么?它有哪些引擎?
- 第17講:MySQL 的優化方案有哪些?
- 第18講:關系型數據和文檔型數據庫有什么區別?
- 第19講:Redis 的過期策略和內存淘汰機制有什么區別?
- 第20講:Redis 怎樣實現的分布式鎖?
- 第21講:Redis 中如何實現的消息隊列?實現的方式有幾種?
- 第22講:Redis 是如何實現高可用的?
- 模塊四:Java 進階
- 第23講:說一下 JVM 的內存布局和運行原理?
- 第24講:垃圾回收算法有哪些?
- 第25講:你用過哪些垃圾回收器?它們有什么區別?
- 第26講:生產環境如何排除和優化 JVM?
- 第27講:單例的實現方式有幾種?它們有什么優缺點?
- 第28講:你知道哪些設計模式?分別對應的應用場景有哪些?
- 第29講:紅黑樹和平衡二叉樹有什么區別?
- 第30講:你知道哪些算法?講一下它的內部實現過程?
- 模塊五:加分項
- 第31講:如何保證接口的冪等性?常見的實現方案有哪些?
- 第32講:TCP 為什么需要三次握手?
- 第33講:Nginx 的負載均衡模式有哪些?它的實現原理是什么?
- 第34講:Docker 有什么優點?使用時需要注意什么問題?
- 彩蛋
- 彩蛋:如何提高面試成功率?