工作中總是遇到數據存儲相關的 Bug 工單,新需求開發設計中也多多少少會有數據模型設計和存儲相關的問題。經過幾次存儲方案設計選型和討論後發現需要有更全面的思考框架。 日常開發中常用的存儲方案選型很多都是 “拿來主義” 的,憑藉著經驗、習慣選用,但對它們的細節特性或約束少有研究。 除了手邊會用的存儲方... ...
工作中總是遇到數據存儲相關的 Bug 工單,新需求開發設計中也多多少少會有數據模型設計和存儲相關的問題。經過幾次存儲方案設計選型和討論後發現需要有更全面的思考框架。
-
日常開發中常用的存儲方案選型很多都是 “拿來主義” 的,憑藉著經驗、習慣選用,但對它們的細節特性或約束少有研究。
-
除了手邊會用的存儲方案,也應該關註市面上更合適的存儲方案。
-
一定的技術預研和儲備能夠幫助未來更好的技術方案設計。
故寫了這篇文章,拋出我的觀察和思考,希望日後可以將一些更先進 (合適) 的技術引入公司業務,助力業務發展。
存儲選型的考慮要素
存儲選型的目的還是為了我們的使用場景和用戶服務,因此在選型前需要回答一些業務指標 & 技術指標方面的問題,以便於我們清楚存儲選型的應用環境。
-
用戶量:用戶量預估多少?幾百幾萬還是幾億?
-
數據量:數據量預估多少?日均增量能有多少?
-
讀寫偏好:數據是讀多一些還是寫多一些?
-
數據場景:強事務型還是分析型需求?
-
運行性能要求:併發量是多少?高峰、平均、低谷分別預估是多少?
存儲引擎分類及特性
資料庫的分類方式非常多樣,因參考維度不同而存在較大差異,下麵是常見的一些分類。
先拿我們最熟悉的關係資料庫來說,它的優點非常多,我們選用關係資料庫的理由可簡單概括為以下幾點:
-
容易理解
可由二維表結構來邏輯表達,相對網狀、層次等其他模型更加容易被理解。嚴格遵循數據格式與長度規範,數據以行為單位,一行數據表示一個實體信息,每一行數據的屬性都是相同的。
-
事務特性
支持 ACID 特性,可以維護數據之間的一致性,這是使用關係資料庫非常重要的一個理由。
-
操作方便
通用的 SQL 語言使得操作關係型資料庫非常方便,支持 join 等複雜查詢,Sql + 二維關係是關係型資料庫最無可比擬的優點,這種易用性非常貼近開發者。
-
數據穩定
數據持久化到磁碟,沒有丟失數據風險。
-
服務穩定
最常用的關係型資料庫產品 MySql、Oracle 伺服器性能卓越,服務穩定,通常很少出現宕機異常。
然而,在享受關係資料庫帶來的便利的同時,我們也不得不面臨很多麻煩的問題:
-
高併發下資料庫瓶頸明顯
數據按行存儲,即使只針對某一列進行運算,也會將整行數據從存儲設備中讀入記憶體,導致 IO 較高。寫入更新頻繁的情況下,資料庫往往會出現 CPU 飆高、Sql 執行慢、客戶端報資料庫連接池不夠等異常情況,且性能瓶頸通過加 CPU、換固態硬碟、繼續買伺服器加資料庫做分庫等方式處理 ROI 不高,受限於其本身的特點,可能花了很多錢都未必能達到想要的效果。因此例如萬人秒殺這種場景,我們絕對不可能通過資料庫直接去扣減庫存,需要做好流量漏斗。
-
為維護數據一致性付出的代價大
數據一致性是關係型資料庫的核心,但是同樣為了維護數據一致性的代價也非常大。SQL 標準為事務定義了不同的隔離級別,從低到高依次是讀未提交、讀已提交、可重覆度、串列化,事務隔離級別越低,可能導致的併發異常越多,但是能提供的併發能力越強。那麼為了保證事務一致性,資料庫就需要提供併發控制與故障恢復兩種技術,前者用於減少併發異常,後者可以在系統異常的時候保證事務與資料庫狀態不會被破壞。對於併發控制,其核心思想就是加鎖,無論是樂觀鎖還是悲觀鎖,只要提供的隔離級別越高,那麼讀寫性能必然會受影響。
-
為維護索引付出的代價大
為了提供豐富的查詢能力,通常熱點表都會有多個二級索引,一旦有了二級索引,數據的新增必然伴隨著所有二級索引的新增,數據的更新也必然伴隨著所有二級索引的更新,這不可避免地降低了關係型資料庫的讀寫能力,且索引越多讀寫能力越差。除了數據文件不可避免地占空間外,索引占的空間其實也並不少。
-
水平擴展後帶來的種種問題難處理
隨著業務規模擴大,一種方式是對資料庫做分庫,做了分庫之後,數據遷移(1 個庫的數據按照一定規則打到 2 個庫中)、跨庫 join、分散式事務處理都是需要考慮的問題,尤其是分散式事務處理,業界當前都沒有特別好的解決方案。
-
全文搜索功能弱
例如 like “% 新年快樂 %”,只能搜索到 “新年快樂,愛大家”,無法搜索到 “新年真是太快樂了,愛大家” 這樣的文本,即不具備分詞能力,且 like 查詢在 “% 新年快樂” 這樣的搜索條件下,無法命中索引,將會導致查詢效率大大降低。
-
表結構擴展不方便
由於資料庫存儲的是結構化數據,因此表結構 schema 是固定的,擴展不方便,如果需要修改表結構,需要執行 DDL(data definition language)語句修改,修改期間會導致鎖表,部分服務不可用。
如上文所分析的,關係型資料庫優點明顯,缺點同樣不能忽視,因此通常在企業規模不斷擴大的情況下,不會一味指望通過增強資料庫的能力來解決數據存儲問題,而是會引入其他存儲,也就是我們說的 NoSql。
NoSql 的全稱為 Not Only SQL,泛指非關係型資料庫,是對關係型資料庫的一種補充,特別註意補充這兩個字,這意味著 NoSql 與關係型資料庫並不是對立關係,二者各有優劣,取長補短,在合適的場景下選擇合適的存儲引擎才是正確的做法。
下麵看一下常用的 NoSql 及他們的代表產品,並對每種 NoSql 的優缺點和適用場景做一下分析,便於熟悉每種 NoSql 的特點,方便技術選型。
1、KV 型 NoSql(代表——Redis)
KV 型 NoSql 顧名思義就是以鍵值對形式存儲的非關係型資料庫,是最常見的一種 NoSql。Redis、MemCache 是其中的代表,Redis 又是 KV 型 NoSql 中應用最廣泛的 NoSql,KV 型資料庫以 Redis 為例,最大的優點總結下來主要有兩點:
-
數據基於記憶體,讀寫效率高。
-
KV 型數據,時間複雜度為 O(1),查詢速度快。
所以說,KV 型 NoSql 最大的優點就是高性能,利用 Redis 自帶的 BenchMark 做基準測試,TPS 可達到 10 萬的級別,性能非常強勁。同樣的 Redis 也有所有 KV 型 NoSql 都有的比較明顯的缺點:
-
記憶體是有限的,無法支持海量數據存儲。
-
只能根據 K 查 V,無法根據 V 查 K。
-
查詢方式單一,只有 KV 的方式,不支持條件查詢,多條件查詢唯一的做法就是數據冗餘,但這會浪費很多存儲空間。
-
由於 KV 型 NoSql 的存儲是基於記憶體的,會有丟失數據的風險(有持久化存儲方案)。
綜上所述,KV 型 NoSql 最合適的場景就是緩存的場景:
-
讀遠多於寫。
-
沒有持久化的需求,可以容忍數據丟失。
針對那些讀遠多於寫的數據,引入一層緩存,每次讀從緩存中讀取,緩存中讀取不到,再去資料庫中取,取完之後再寫入到緩存,對數據做好失效機制通常就沒有大問題了。通常來說,緩存是性能優化的第一選擇也是見效最明顯的方案。
2、搜索型 NoSql(代表——ElasticSearch)
傳統關係型資料庫主要通過索引來達到快速查詢的目的,但是在全文搜索的場景下,索引是無能為力的,like 查詢無法滿足所有模糊匹配需求,使用限制太大且使用不當容易引起慢查詢問題,搜索型 NoSql 的誕生正是為瞭解決關係型資料庫全文搜索能力較弱的問題,ElasticSearch 是搜索型 NoSql 的代表產品。
全文搜索的原理是倒排索引,我們看一下什麼是倒排索引,它是關鍵字 –> 文檔的映射,舉例來說,現在這裡有四個短句:
-
"Tom is Tom"
-
"Tom is my friend"
-
"Thank you, Betty"
-
"Tom is Betty's husband"
搜索引擎會根據一定的分詞規則將一句話切成多個關鍵字,並以關鍵字的維度維護關鍵字在每個文本中的出現次數。這樣下次搜索“Tom”關鍵字的時候,由於 Tom 這個詞語在“Tom is Tom”、“Tom is my friend”、“Tom is Betty’s husband” 三句話中都出現過,因此這三條記錄都會被檢索出來,而且由於”Tom is Tom” 這句話中”Tom” 出現了 2 次,因此這條記錄對”Tom” 這個單詞的匹配度最高,最先展示。這就是搜索引擎倒排索引的基本原理,假設某個關鍵字在某個文檔中出現,那麼倒排索引中有兩部分內容:
-
文檔 ID
-
該關鍵字在該文檔中出現的位置情況
相對應的,我們搜索”Betty Tom” 這兩個詞語也是一樣,搜索引擎將”Betty Tom” 切分為”Tom”、”Betty” 兩個單詞,根據開發者指定的滿足率,比如滿足率 = 50%,那麼只要記錄中出現了兩個單詞之一的記錄都會被檢索出來,再按照匹配度進行展示。
搜索型 NoSql 以 ElasticSearch 為例,它的優點為:
1)支持分詞場景、全文搜索,這是區別於關係型資料庫最大特點。
2)數據寫文件無丟失風險,在集群環境下可以方便橫向擴展,可承載 PB 級別的數據。
3)支持條件查詢,支持聚合操作,類似關係型資料庫的 Group By,但是功能更加強大,適合做數據分析。
4)高可用,自動發現新的或者失敗的節點,重組和重新平衡數據,確保數據是安全和可訪問的。
同樣,ElasticSearch 也有比較明顯的缺點:
1)性能全靠記憶體來頂,也是使用的時候最需要註意的點,非常吃記憶體,大數據量下 64G + SSD 基本就是標配,相同的配置多一倍記憶體,一個月差不多就要多花好多錢。至於 ElasticSearch 記憶體主要用在以下幾個地方:
-
Indexing Buffer----ElasticSearch 基於 Luence,Lucene 的倒排索引是先在記憶體里生成,然後定期以 Segment File 的方式刷磁碟的,每個 Segment File 實際就是一個完整的倒排索引。
-
各類緩存 ----Filter Cache、Field Cache、Indexing Cache 等,用於提升查詢分析性能,例如 Filter Cache 用於緩存使用過的 Filter 的結果集。
-
Segment Memory---- 倒排索引前面說過是基於關鍵字的,Lucene 在 4.0 後會將所有關鍵字以 FST 這種數據結構的方式將所有關鍵字在啟動的時候全量載入到記憶體,加快查詢速度,官方建議至少留系統一半記憶體給 Lucene。
-
Cluter State Buffer----ElasticSearch 被設計為每個 Node 都可以響應用戶請求,因此每個 Node 的記憶體中都包含有一份集群狀態的拷貝,一個規模很大的集群這個狀態信息可能會非常大。
2)數據結構靈活性不高,欄位一旦建立就沒法修改類型了,假如建立的數據表某個欄位沒有加全文索引,想加上,那麼只能把整個表刪了再重建。
3)讀寫之間有延遲,寫入的數據差不多 1s 樣子會被讀取到(數據寫入時需要維護很多索引)。
因此,搜索型 NoSql 最適用的場景就是有條件搜索尤其是全文搜索的場景,作為關係型資料庫的一種替代方案,通常搜索型 NoSql 也會作為一層前置緩存,來對關係型資料庫進行保護。
此外,搜索型資料庫還有一種非常重要的應用場景。我們可以想,一旦對資料庫做了分庫分表後,原來可以在單表中做的聚合操作、統計操作是否統統失效?例如我把訂單表分 16 個庫,1024 張表,那麼訂單數據就散落在 1024 張表中,我想要統計昨天浙江省單筆成交金額最高的訂單是哪筆如何做?這就是搜索型 NoSql 的另一大作用了,我們可以把分表之後的數據統一打在搜索型 NoSql 中,利用搜索型 NoSql 的搜索與聚合能力完成對全量數據的查詢。
3、列式 NoSql(代表——HBase)
列式 NoSql 和關係型資料庫一樣都有主鍵的概念,區別在於關係型資料庫是按照行組織的數據,數據欄位即使沒有值同樣占空間,列式存儲完全是另一種方式,它是按列進行數據組織的,好處在於:
-
查詢時只有指定的列會被讀取,不會讀取所有列。
-
存儲上節約空間,空值不會被存儲,一列中有時候會有很多重覆數據(尤其是枚舉數據,性別、狀態等欄位),這類數據可壓縮。
-
列數據被組織到一起,一次磁碟 IO 可以將一列數據一次性讀取到記憶體中。
大數據時代最具代表性的技術之一 HBase 就是列式 NoSQL 的產品實現,其優點主要是:
-
海量數據存儲,PB 級別數據隨便存,底層基於 HDFS(Hadoop 文件系統),數據持久化。
-
讀寫性能好,只要沒有濫用造成數據熱點,讀寫基本沒任何問題。
-
橫向擴展在關係型資料庫及非關係型資料庫中都是最方便的之一,只需要添加新機器就可以實現數據容量的線性增長,且可用在廉價伺服器上,節省成本。
-
可存儲結構化或者半結構化的數據。
-
本身沒有單點故障,可用性高。
-
列數理論上無限制,HBase 本身只對列族數量有要求,建議 1~3 個。
缺點主要表現在:
-
HBase 是 Hadoop 生態的一部分,因此它本身是一款比較重的產品,依賴很多 Hadoop 組件,數據規模不大沒必要用,運維還是有點複雜的。
-
不支持分頁查詢,因為統計不了數據總數。
-
KV 式存儲,條件查詢很弱,HBase 在 Scan 掃描一批數據的情況下還是提供了首碼匹配這種 API 的,條件查詢除非定義多個 RowKey 做數據冗餘。
因此 HBase 比較適用於 KV 型存儲且未來無法預估數據增長量的場景,另外 HBase 使用還是需要一定的經驗,主要體現在 RowKey 的設計上。
4、文檔型 NoSql(代表——MongoDB)
文檔型 NoSql 指的是將半結構化數據存儲為文檔的一種 NoSql,文檔型 NoSql 通常以 JSON 或者 XML 格式存儲數據,因此文檔型 NoSql 是沒有 Schema 的,由於沒有 Schema 的特性,我們可以隨意地存儲與讀取數據,因此文檔型 NoSql 的出現是解決關係型資料庫表結構擴展不方便的問題的。
MongoDB 是文檔型 NoSql 的代表產品,同時也是所有 NoSql 產品中的明星產品之一,它的很多概念與關係資料庫類似,因此,對於 MongDB,我們只需要理解成一個 Free-Schema 的關係型資料庫就好了,其優點主要是:
-
沒有預定義的欄位,擴展欄位容易。
-
相較於關係型資料庫,讀寫性能優越,命中二級索引的查詢不會比關係型資料庫慢,對於非索引欄位的查詢則是全面勝出。
缺點在於:
-
不支持事務操作,雖然 Mongodb4.0 之後宣稱支持事務,但是效果待觀測。
-
多表之間的關聯查詢不支持(雖然有嵌入文檔的方式),join 查詢還是需要多次操作。
-
空間占用較大,這個是 MongDB 的設計問題,空間預分配機制 + 刪除數據後空間不釋放,只有用 db.repairDatabase () 去修複才能釋放。
-
目前沒發現 MongoDB 有關係型資料庫例如 MySql 的 Navicat 這種成熟的運維工具。
總而言之,MongDB 的使用場景很大程度上可以對標關係型資料庫,但是比較適合處理那些沒有 join、沒有強一致性要求且表 Schema 會常變化的數據。
通過以上討論分析我們心中已經有了一個基本的選型框架指導,實際上在資料庫選型時回答自己兩個核心問題就好了:
-
什麼時候選用關係型資料庫,什麼時候選用非關係型資料庫。
-
選用非關係型資料庫的話,使用哪種非關係型資料庫。
NoSQL 資料庫都是通過犧牲了 ACID 特性來獲取更高性能的,假設表數據有很強的事務特性需求,那麼這類數據是不適合放在非關係型資料庫。此外,選用 NoSQL 資料庫時也要根據公司技術棧框架、業務特性、運維成本等多方面考慮是否採納。
總結
關係型資料庫和 NoSQL 資料庫的選型,往往需要考慮幾個指標:
-
數據量
-
併發量
-
實時性
-
一致性要求
-
讀寫分佈和類型
-
安全性
-
運維成本
常見軟體系統資料庫選型參考如下:
-
中後臺管理型系統 - 如運營系統,數據量少,併發量小,首選關係型資料庫。
-
大流量系統 - 如電商單品頁,後臺考慮選關係型資料庫,前臺考慮選記憶體型資料庫。
-
日誌型系統 - 原始數據考慮選列式資料庫,日誌搜索考慮選搜索引擎。
-
搜索型系統 - 例如站內搜索,非通用搜索,如商品搜索,後臺考慮選關係型資料庫,前臺考慮選搜索引擎。
-
事務型系統 - 如庫存,交易,記賬,考慮選關係型資料庫 + K-V 資料庫(作為緩存)+ 分散式事務。
-
離線計算 - 如大量數據分析,考慮選列式資料庫或關係型資料庫。
-
實時計算 - 如實時監控,可以考慮選記憶體型資料庫或者列式資料庫。
設計實踐中,要基於需求、業務驅動架構,無論選用 RDB/NoSQL, 一定是以需求為導向,最終數據存儲方案必然是各種權衡的綜合性設計。
作者丨August Rush
本文來自博客園,作者:古道輕風,轉載請註明原文鏈接:https://www.cnblogs.com/88223100/p/Relational-and-non-relational-database-storage-selection-inventory.html