1.前言
老是聊併發的話題,聊到你們都免疫了,因此此次串講下個話題——數據庫(歡迎糾正補充)html
看完問本身一個問題來自我檢測:NoSQL我到底該怎麼選?git
數據庫排行:https://db-engines.com/en/rankinggithub
1.1.分類
主要有這麼三大類:(再老的數據庫就不說了)redis
1.傳統數據庫(SQL):
- 關係數據庫:SQLite、MySQL、SQLServer、PostgreSQL、Oracle...
2.高併發產物(NoSQL):
- 鍵值數據庫:Redis、MemCached...
- PS:如今
LevelDB
和RocksDB
也是比較高效的解決方案
- PS:360開發的
pika
(redis的補充)能夠解決Redis容量過大的問題
- 文檔數據庫:MongoDB、ArangoDB、CouchBase、CouchDB、RavenDB...
- PS:Python有一款基於json存儲的輕量級數據庫:
tinydb
- 列式數據庫:Cassandra、HBase、BigTable...
- 搜索引擎系:Elasticsearch、Solr、Sphinx...
- PS:這幾年用Rust編寫的輕量級搜索引擎
sonic
很火
- 圖形數據庫:Neo4J、Flockdb、ArangoDB、OrientDB、Infinite Graph、InfoGrid...
- PS:基於Redis有一款圖形數據庫用的也挺多:
RedisGraph
- PS:隨着Go的興起,這些圖形數據庫很火:Cayley、
Dgraph
、Beam
PS:項目中最經常使用的其實就是Redis
、Elasticsearch
、MongoDB
算法
PS:ArangoDB
是一個原生的多模型數據庫,具備文檔,圖形和鍵值的靈活數據庫sql
3.新時代產物(TSDB):
- 時序數據庫:InfluxDB、LogDevice、Graphite、、OpenTSDB...
來看個權威的圖:(紅色的是推薦NoSQL,灰色是傳統SQL)
數據庫
1.2.概念
先說下NoSQL不是不要使用傳統SQL了,而是不只僅是傳統的SQL(not only sql)json
1.關係型數據庫優劣
先看看傳統數據庫的好處:緩存
- 經過事務保持數據一致
- 能夠Join等複雜查詢
- 社區完善(遇到問題簡單搜下就ok了)
固然了也有不足的地方:服務器
- 數據量大了的時候修改表結構。eg:加個字段,若是再把這個字段設置成索引那是卡到爆,徹底不敢在工做時間搞啊
- 列不固定就更蛋疼了,通常設計數據庫不可能那麼完善,都是後期愈來愈完善,就算本身預留了
保留字段
也不人性化啊
- 大數據寫入處理比較麻煩,eg:
- 數據量不大還好,批量寫入便可。
- 但是自己數據量就挺大的,進行了
主從複製
,讀數據在Salver
進行到沒啥事,可是大量寫數據庫懟到Master
上去就吃不消了,必須得加主數據庫了。
- 加完又出問題了:雖然把主數據庫一分爲二,可是容易發生
數據不一致
(一樣數據在兩個主數據庫更新成不同的值),這時候得結合分庫分表,把表分散在不一樣的主數據庫中。
- 完了嗎?NoNoNo,想想表之間的Join咋辦?豈不是要跨數據庫和跨服務器join了?簡直就是拆東牆補西牆的節奏啊,因此各類中間件就孕育而生了【SQLServer這方面擴展的挺不錯的,列存儲也自帶,也跨平臺了(建議在Docker中運行)(點我查看幾年前寫的一篇文章)】
- 歡迎補充~(說句良心話,中小型公司
SQLServer
絕對是最佳選擇,能省去不少時間)
2.NoSQL
如今說說NoSQL了:(其實你能夠理解爲:NoSQL就是對原來SQL的擴展補充)
- 分表分庫的時候通常把關聯的表放在同一臺服務器上,這樣便於join操做。而NoSQL不支持join,反而不用這麼侷限了,數據更容易分散存儲
- 大量數據處理這塊,讀方面傳統SQL並無太多劣勢,NoSQL主要是進行緩存處理,批量寫數據方面測試每每遠高於傳統SQL,並且NoSQL在擴展方面方便太多了
- 多場景類型的NoSQL(鍵值,文檔、列、圖形)
若是仍是不清楚到底怎麼選擇NoSQL,那就再詳細說說每一個類型的特色:
- 鍵值數據庫:這個你們很熟悉,主要就是
鍵值存儲
,表明=>Redis(支持持久化和數據恢復,後面咱們會詳談)
- 文檔數據庫:表明=>MongoDB(優酷的在線評論就是用基於MongoDB的)
- 通常都不具有事務(
MongoDB 4.0
開始支持ACID
事務了)
- 不支持Join(Value是一個可變的
類JSON格式
,表結構修改比較方便)
- 列式數據庫:表明:Cassandra、
HBase
- 對大量行少許列進行修改更新(新增一字段,批量作個啥操做的不要太方便啊~針對列爲單位讀寫)
- 擴展性高,數據增長也不會下降對應的處理速度(尤爲是寫)
- 搜索引擎系:表明:Elasticsearch,太經典了不用說了(傳統模糊搜索只能like太low,因此有了這個)
- 圖形數據庫:表明:Neo4J、Flockdb、ArangoDB(數據模型是圖結構的,主要用於 關係比較複雜 的設計,好比繪製一個QQ羣關係的可視化圖、或者繪製一個微博粉絲關係圖等)

回頭仍是要把併發剩餘幾個專題深刻的,認真看的同志會發現無論什麼語言底層實現都是差很少的。
好比說進程,其底層就是用到了咱們第一講說的OS.fork
。再說進(線)程通訊,講的PIPE、FIFO、Lock、Semaphore
等不多用吧?可是Queue
底層就是這些實現的,不清楚的話怎麼讀源碼?
還記得當時引入Queue篇
提到Java裏的CountDownLatch
嗎?要是不瞭解Condition
怎麼本身快速模擬一個Python裏面沒有的功能呢?
知其然不知其因此然是萬萬不可取的。等後面講MQ的時候又得用到Queue
的知識了,可謂一環套一環~
既然不是公司的萌妹子,因此呢~技術的提高仍是得靠本身了^_^,先到這吧,最後貼個經常使用解決方案:
Python、NetCore經常使用解決方案(持續更新)
https://github.com/LessChina
2.概念
上篇提到了ACID
此次準備說說,而後再說說CAP
和數據一致性
2.1.ACID事務
以小明和小張轉帳的例子繼續說說:
- A:原子性(Atomic)
- 小明轉帳1000給小張:小明-=1000 => 小張+=1000,這個 (事務)是一個不可分割的總體,若是小明-1000後出現問題,那麼1000得退給小明
- C:一致性(Consistent)
- 小明轉帳1000給小張,必須保證小明+小張的總額不變(假設不受其餘轉帳(事務)影響)
- I:隔離性(Isolated)
- 小明轉帳給小張的同時,小潘也轉錢給了小張,須要保證他們相互不影響(主要是併發狀況下的隔離)
- D:持久性(Durable)
- 小明轉帳給小張銀行要有記錄,即便之後扯皮也能夠拉流水帳【事務執行成功後進行的持久化(就算數據庫以後掛了也能經過Log恢復)】
2.2.CAP概念
CAP是分佈式系統須要考慮的三個指標,數據共享只能知足兩個而不可兼得:

- C:一致性(Consistency)
- 全部節點訪問同一份最新的數據副本(在分佈式系統中的全部數據備份,在同一時刻是否一樣的值)
- eg:分佈式系統裏更新後,某個用戶都應該讀取最新值
- A:可用性(Availability)
- 在集羣中一部分節點故障後,集羣總體是否還能響應客戶端的讀寫請求。(對數據更新具有高可用性)
- eg:分佈式系統裏每一個操做總能在必定時間內返回結果(超時不算【網購下單後一直等算啥?機房掛幾個服務器也不影響】)
- P:分區容錯性(Partition Toleranc)
- 以實際效果而言,分區至關於對通訊的時限要求。系統若是不能在時限內達成數據一致性,就意味着發生了分區的狀況,必須就當前操做在C和A之間作出選擇。
- eg:分佈式系統裏,存在網絡延遲(分區)的狀況下依舊能夠接受知足一致性和可用性的請求
CA
表明:傳統關係型數據庫
若是想避免分區容錯性問題的發生,一種作法是將全部的數據(與事務相關的)都放在一臺機器上。雖然沒法100%保證系統不會出錯,但不會碰到由分區帶來的負面效果(會嚴重的影響系統的擴展性)
做爲一個分佈式系統,放棄P,即至關於放棄了分佈式,一旦併發性很高,單機服務根本不能承受壓力。像不少銀行服務,確確實實就是捨棄了P,只用高性能的單臺小型機保證服務可用性。(全部NoSQL數據庫都是假設P是存在的)
CP
表明:Zookeeper
、Redis
(分佈式數據庫、分佈式鎖)
相對於放棄「分區容錯性「來講,其反面就是放棄可用性。一旦遇到分區容錯故障,那麼受到影響的服務須要等待數據一致
(等待數據一致性期間系統沒法對外提供服務)
AP
表明:DNS數據庫
(IP和域名相互映射的分佈式數據庫
,聯想修改IP後爲何TTL
須要10分鐘左右保證全部解析生效)

反DNS查詢:http://www.javashuo.com/article/p-uvuapttc-s.html
放棄強一致,保證最終一致性。全部的NoSQL
數據庫都是介於CP
和AP
之間,儘可能往AP
靠,(傳統關係型數據庫注重數據一致性,而對海量數據的分佈式處理來講可用性和分區容錯性優先級高於數據一致性)eg:
不一樣數據對一致性要求是不同的,eg:
- 用戶評論、彈幕這些對一致性是不敏感的,很長時間不一致性都不影響用戶體驗
- 像商品價格等等你敢來個看看?對一致性是很高要求的,容忍度鐵定低於10s,就算使用了緩存在訂單裏面價格也是最新的(平時注意下JD商品下面的緩存說明,JD尚且如此,其餘的就不用說了)

2.3.數據一致性
傳統關係型數據庫通常都是使用悲觀鎖的方式,可是例如秒殺這類的場景是hou不動,這時候每每就使用樂觀鎖了(CAS機制,以前講併發和鎖的時候提過),上面也稍微提到了不一樣業務需求對一致性有不一樣要求而CAP不能同時知足,這邊說說主要就兩種:
- 強一致性:不管更新在哪一個副本上,以後對操做都要可以獲取最新數據。多副本數據就須要
分佈式事物來保證數據一致性
了(這就是問什麼項目裏面常常提到的緣由)
- 最終一致性:在這種約束下保證用戶最終能讀取到最新數據。舉幾個例子:
- 因果一致性:A、B、C三個獨立進程,A修改了數據並通知了B,這時候B獲得的是最新數據。由於A沒通知C,因此C不是最新數據
- 會話一致性:用戶本身提交更新,他能夠在會話結束前獲取更新數據,會話結束後(其餘用戶)可能不是最新的數據(提交後JQ修改下本地值,不能保證數據最新)
- 讀自寫一致性:和上面差很少,只是不侷限在會話中了。用戶更新數據後他本身獲取最新數據,其餘用戶可能不是最新數據(必定延遲)
- 單調讀一致性:用戶讀取某個數值,後續操做不會讀取到比這個數據還早的版本(新的程度>=讀取的值)
- 單調寫一致性(時間軸一致性):全部數據庫的全部副本按照相同順序執行全部更新操做(有點像
Redis
的AOF
)
2.4.一致性實現方法
Quorum
系統NRW
策略(經常使用)
Quorum是集合A,A是全集U的子集,A中任意取集合B、C,他們二者都存在交集。
NRW算法:
- N:表示數據所具備的副本數。
- R:表示完成讀操做所須要讀取的最小副本數(一次讀操做所需參與的最小節點數)
- W:表示完成寫操做所須要寫入的最小副本數(一次寫操做所須要參與的最小節點數)
- 只須要保證
R + W > N
就能夠保證強一致性(讀取數據的節點和被同步寫入的節點是有重疊的)好比:N=3,W=2,R=2(有一個節點是讀+寫)
擴展:
- 關係型數據庫中,若是N=2,能夠設置W=2,R=1(寫耗性能一點),這時候系統須要兩個節點上數據都完成更新才能確認結果並返回給用戶
- 若是
R + W <= N
,這時候讀寫不會在一個節點上同時出現,系統只能保證最終一致性。副本達到一致性的時間依賴於系統異步更新的方式,不一致性時間=從更新節點~全部節點都異步更新完畢的耗時
- R和W設置直接影響系統的性能、擴展和一致性:
- 若是W設置爲1,那麼一個副本更新完就返回給用戶,而後經過異步機制更新剩餘的N-W個節點
- 若是R設置爲1,只要有一個副本被讀取就能夠完成讀操做,R和W的值若是較小會影響一致性,較大會影響性能
- 當W=1,R=N==>系統對寫要求高,但讀操做會比較慢(N個節點裏面有1個掛了,讀就完成不了了)
- 當R=1,W=N==>系統對讀操做有高要求,但寫性能就低了(N個節點裏面有1個掛了,寫就完成不了了)
- 經常使用方法:通常設置
R = W = N/2 + 1
,這樣性價比高,eg:N=3,W=2,R=2(3個節點==>1寫,1讀,1讀寫
)
參考文章:
http://book.51cto.com/art/201303/386868.htm
https://blog.csdn.net/jeffsmish/article/details/54171812
時間軸策略(經常使用)
- 主要是關係型數據庫的日記==>記錄事物操做,方便數據恢復
- 還有就是並行數據存儲的時候,因爲數據是分散存儲在不一樣節點的,對於同一節點來講只要關心
數據更新+消息通訊
(數據同步):
- 保證較晚發生的更新時間>較早發生的更新時間
- 消息接收時間 > 消息發送時刻的時間(要考慮服務器時間差的問題~時間同步服務器)
其餘策略
其實還有不少策略來保證,這些概念的對象逆天不是很熟~好比:向量時鐘策略
推薦幾篇文章
:
https://www.cnblogs.com/yanghuahui/p/3767365.html
http://blog.chinaunix.net/uid-27105712-id-5612512.html
https://blog.csdn.net/dellme99/article/details/16845991
https://blog.csdn.net/blakeFez/article/details/48321323