實在不知道起什麼名字,打算寫三個系列的文章,由於系統儘可能有狀態和無狀態分離,第一個想寫下系統中涉及到數據存儲/分析/選型/本身設計要怎麼作,包含內存/持久,數據量單機到大量到海量分佈式的存儲和計算。第二個想寫無狀態系統如何作到高處理能力,涉及到網絡/分流/通訊,進程/線程模型,操做系統IO,CPU調度等等。第三個爲C++系列基礎編碼。
本篇爲第一部分,先說下非海量數據存儲選型應該考慮的點,本身設計也能夠參考的點,對比了經常使用的非海量數據存儲軟件,這部分對數據模型,索引/存儲,分佈式的可靠性,可擴展,一致性給出總結性的解決方案。還介紹了海量數據(可能通常核心數據沒這麼多,但衍生數據量大)和流數據的存儲和計算的總結性解決方案,這其中涉及衆多組件,我只用過少部分,爲了直觀性,給出了幾種常見衍生數據處理系統的應用設計。
系列文章涵蓋了文中提到或未提到但常見的內容都分別單獨篇章。node
存儲應該考慮的點: ——性能(存儲介質,數據格式,數據組織,索引,cache) ——功能(索引,事務) ——一致性 ——可靠性 ——擴展性(對於scale up共享內存和磁盤部分忽略) ——成本(物理,維護) 其中成本是須要再各個指標中關聯的,好比性能裏介質壓縮大小,內存佔用,可靠性纔會冗餘備份等,不單獨討論了。
影響性能的:存儲介質,數據模型,索引,存儲格式。mysql
存儲介質 | 特色 | 數據組織 |
---|---|---|
磁帶 | ||
內存 | map,set,list,skip-list,memory-table,stm(支持內存事務) | |
磁盤 | 順序讀寫強,隨機讀寫差,block | -Tress =>B+ 層數同樣,性能穩定,中間節點只有索引,容易緩存,數據只在子節點,數據能夠掃描 |
SSD | 隨機性能高,並行度高,擦除影響壽命 | SST(sst爲什麼適合SSD?SSD是併發寫,適合一次寫入大量,擦除影響壽命,但願更新少,由於併發隨機讀寫能力更強) |
PCM |
散列表必需要內存中放得下
,不然須要大量隨機訪問,不支持範圍查詢2.內存放不下,hash索引=》稀疏索引+有序
SSTables:sorting string table 要求每一個鍵只在每一個段中出現一次且排序能夠超出內存(隨加載隨merge),不須要保存全部鍵的索引(排序),範圍查找
如何讓數據有序:磁盤上B樹,內存中更容易:紅黑樹/AVL樹
LSM,日誌合併,保存一系列在後臺合併的SSTable,寫入時,添加到內存的紅黑樹中。做爲SSTable寫入磁盤。關鍵在於會如何壓縮和合並
LevelDB 水平,HBase 大小分層
3.從日誌追加到就地更新=》B樹
寫操做用新數據覆蓋磁盤頁面,併發控制複雜。要等一整頁一塊兒寫,須要用額外的磁盤(redo log)[二次寫,若不這樣作須要控制塊對其和大小寫入512一次或標識頭部等,實際上由於內存延遲刷新,WAL只要有內存延遲都配了]。LSM只是追加
一些優化:修改頁面寫入不一樣位置,父頁面建立新版本(mongodb)。B+樹等
比較B樹與LSM
B 讀取速度快。對於兩個邏輯相近的頁物理可能很遠,隨機讀寫多
LSM 寫入速度快,非頁單位寫入量大,都是順序寫。碎片少。LSM因爲壓縮更小。SSTbales在合併時複寫(有時應爲磁盤寫入帶寬等有限,有影響,壓縮和寫入速率控制)。
4.其餘索引:多列索引(B樹和LSM都不能很好的支持);空間,二維填充曲線轉爲單個數字再用B樹索引,或者用特殊化的空間索引R樹;全文索引/模糊索引,lucene
5.內存中存儲一切:性能+實現特殊的磁盤和索引難以實現的數據模型好比隊列。=》反緩存web
功能各個系統本身針對不一樣會有很大差異(排序,聚合,搜索等),通用的功能只有事務,這裏作下討論,其餘特性能夠查API,功能和使用通常文檔都比較好查,不仔細說了redis
單獨說可靠性指異常補償,涉及到複製,故障發現,故障自動轉移。比較簡單。有些副本提供讀服務還有多寫副原本提高可用性,有時存儲的可靠性和可用性能夠一塊兒解決,簡單說下這種部署和延時問題,詳細的一致性在下面說。算法
多活多數據中心與單活對比:單活每一個寫入都要進入主庫數據中心,增長寫入時間;經過網絡中心的寫是同步的,對網絡容忍度比多中心異步複製低不少;故障一個主從切換,一個換主。
處理寫入衝突:寫入檢測兩個主違背多主目的,寫入都成功後同步時檢測衝突用戶沒法補救。
併發寫入:哪些須要覆蓋(好比依賴關係的B知道A先發生,只須要覆蓋便可),哪些是併發(B不知道A的發生,併發須要按版本解決或者容許丟失用時間或序號。)
=》
預防衝突:同一寫入只入一個,在故障等時特殊處理(須要切換,保證不了同一個請求不丟失或不重複)。
衝突合併:每一個寫入或副本一個惟一ID,a.覆蓋丟棄,b.維護多版本,自動處理的數據類型:集合等,帶多版本等。
b.版本向量:返回時將讀取自的版本返回,再次寫入時帶該版本的就是依賴(原本知道有這個版本),可覆;不然是併發保留保本(原本不知道有這個版本)。sql
3.無主:所有多讀多寫,返回數量足夠成功。(讀寫法定人數的確認)
單個落後如何恢復:讀取時選擇版本高的反更新(讀修復)或 異步不斷同步差別(反熵,不保證順序)
寫入衝突和多主同樣mongodb
困難:丟包\延遲,時鐘不一樣步。會致使設計上再延時(同步,半同步,異步)和一致性(線性一致性,原子事務提交)上有必定取捨,現實中模型:同步模型,假設網絡延遲,進程暫停,時鐘偏差都有界限;部分同步:大多數同步,偶爾變得至關大;異步模型:不能用超時,沒有時鐘。崩潰-中止故障:中止後永遠不回來\崩潰恢復故障:未知時間後再次開始響應,節點具備穩定的存儲且會保留/內存會丟失。拜占庭故障:包括試圖戲弄和欺騙其餘節點。能夠實現不一樣等級的一致性:如ACID,最終一致性,sessioin一致性,單調一致性
CAP是針對一條數據的(同一個系統數據可根據要求作不一樣處理)。一致性的定義應該是普遍的(不僅是副本之間數據相同),能夠理解爲對一條數據獲取的一致性,多我的多同一條數據讀結果一致,一個事務對同一數據讀結果一致,惟一性讀取一致(惟一被獲取其餘讀應該失敗)
寫入順序與實際一致 不屬於一致性範疇但應儘可能保證才使得讀有意義,也歸到這裏討論。叫因果一致性
最強的一致性是線性一致性(一旦寫入成功讀取的就是該值,直到再次覆蓋)
所以涉及到問題大概有:因果一致性保證,線性一致性保證,事務一致性保證,惟一性約束
一個客戶端在多個寫節點中的順序保證
,多節點寫入時保證同步時因果覆蓋正確。解決同一客戶端發出對同一操做兩個有序請求,最終到兩個主庫上,序號(到達是有序的,但時鐘不可靠)不必定哪一個在後,因此同步時有錯的問題。辦法就是每次請求帶節點最大序號,更新落後的節點。(不需去取全局遞增序號)只是提供同步序號問題,只有同步解決纔會獲得全局數據,讀數據調的應用需阻塞在全局回覆上數據庫
全序廣播
順序在消息傳遞時被固化,不容許將消息插入到順序中較早位置。全序廣播保證以固定順序可靠的發送,不保證消息什麼時候被送達.
全序廣播實現線性一致性存儲apache
向全序日誌中追加一個惟一的用戶名,讀日誌,等待消息被送回時執行。用一個全序日誌作同步(C讀,A讀,B寫,A寫、每一個寫所有節點都執行,讀要等寫都執行完,是本身調的返回客戶端) 寫一致性:若該日誌第一條消息全部權是本身的,確認提交(給客戶端返回)。如有併發寫入,全部節點會對最早到達者達成一致。非本身節點也執行的 讀取一致性:當有讀取時追加一條消息,消息回送時讀取日誌,執行實際的讀取返回。用全序控制線性,串行。
一旦commit沒法rollback
第一階段:收集協商,若參與者返回OK,即保證undo落盤可提交可回滾了。第二階段把redo落盤或者undo回滾
若是協調者在準備好後失敗,不得不等待他從新恢復。協調者上的常規單點提交。協調者有問題,鎖沒法釋放棘手問題.即便協調者從新第二階段沒法判斷是否是要提交,1在線可是隻能知道本身yes,若2失敗沒法獲取2的投票和狀態。
缺陷:協調者單點,引入協調者可能使得服務器再也不是無狀態的不能隨意擴容,當誇各類數據系統時,須要時全部系統的最小集不能檢測系統間死鎖,沒法SSI等,數據庫內部的分佈式事務(其實非XA)沒有3問題可是系統任何部分失敗都會失敗 擴大失效=》改用共識編程
2.三階段分佈式原子提交
只要有一個進入precommit就說明確定能夠commit,不然不會commit。
二階段嚴重依賴C,節點不能根據自身投票決定,C和CL1在圖中位置都掛後,新C不知道是否是都贊成,會使得pre無心義。三階段能夠在加鎖後不依賴C釋放。Pre能說明投票的狀況,因此有了p能夠自動工作,新C根據P決定do,若是C2和C同時失敗,此時C1收到pre則繼續沒收到C1會abort,C2確定也沒有commit。Pre時間必定要設置很長,保證C能夠判斷集羣p的信息,不然若是出現pre最終有收到有沒掛沒收到自動超時仍是有問題。
對於網絡分區能夠用共識解決,恢復重啓須要同步日誌
內存
,多種內存數據結構
無索引,基本不支持事務
1,代碼中寫;
2,redis Cluster。請求不在的key要兩次,先返回ip再請求一次
3,代理分片,好比tuemproxy,codis
1.主從模式。一主多從
RDB文件
+緩衝區命令
寫命令傳播
給從服務器心跳檢測
。檢測網絡和命令丟失。(主服務器配置min-slaves-to-write n, min-slaves-max-lag m當從服務器數量少於3個,或者延遲大於等於10將拒絕執行寫命令根據replication_offset檢測是否丟失命令,補發命令)replication_offset,複製積壓緩衝區
,服務運行ID2.哨兵模式。哨兵系統也是一個或多個特殊的redis服務器,監視普通服務器,負責下線主服務器和故障轉移
哨兵經過raft協議選主,主哨兵選擇主服務器
)。3.集羣模式。去中心化,增長可擴展性,每一個能夠讀寫
gossip通訊,從節點發現故障,raft從新選主
16384個槽。以槽爲單位,從新分片
讀寫到任意節點, 二次轉移
redis-trib
分片:hash
元數據:codis-proxy中,用codis-dashboard控制,zk保持同步
擴展:固定1024個slot。遷移是按照slot的維度
遷移有兩個階段,第一階段狀態改成pre_m。若proxy都確認,將狀態改m。向所在的redis-server發送遷移命
codis-proxy的用zookeeper保證
。client獲取zk節點作負載均衡codis-group的主從用redis的哨兵模式
分片信息和元數據由zk保證一致性,group中主從由redis自身負責最終一致性
詳細redis與codis見:
https://segmentfault.com/a/11...
磁盤,B+樹索引(搜索性能高穩定,節點不包含數據能夠包含更多地址,層高少,葉節點鏈表掃面呢)/主鍵聚簇索引,內存buffer(二級索引change buffer)
buffer到磁盤過程當中問題:
刷髒 flush
二次寫 髒頁落盤須要二次寫,redolog塊對其不須要
清理過時 purge
事務
A undo log,邏輯日誌,受redo log保護 。涉及回滾
C (一個事務中間狀態可見性一致)MVCC 每一個視圖有readviewid。經過undo日誌恢復舊視圖
I (多個事物之間可見性/操做不干擾)MVCC
D redolog 物理位置+邏輯日誌。每一個事務本身buffer=>公共buffer=>磁盤 涉及checkpoint
server和innodb的binlog和redolog的一致性保證:內部二階段提交
分佈式事務
沒有本身的集羣管理.須要自行實現
主從 用binlog複製(基於行,語句,混合);採起同步/半同步/異步;全局GTID代替文件名和物理偏移量使得slave在多線程併發複製時崩潰恢復不會重複執行相同事務操做(GTID這種方式能夠避免重複發,可是找起來沒有文件和物理位置方便,須要記錄集合,用GTID-sets有序合併存儲xx:1-120這種形式,併發避免不了不能用位置由於多線程不按是順序執行)。
當主庫支撐不了。水平擴展。拆表。
沒法自身保證
同步策略影響。
XA分爲外部和內部。對於外部。要應用程序或proxy做爲協調者。(二階段提交協調者判斷全部prepare後commit)。對於內部,binlog控制。
同步和事務失敗回滾會有問題,先提交發送網絡異常致使主庫有從庫無。等發送返回後提交失敗回滾致使從庫有主庫無。
詳細:
https://segmentfault.com/a/11...
沒有開源。基於cache+rocksdb。
介於redis和rocksdb以前。對rocksdb的熱key多了一層cache。
想結合redis的性能,mysql的持久化。redis和mysql的集成。支持分佈式。
master讀寫都在master.slave備份做用。同一個slave和master不在一臺機器上
把全部結構都轉爲純粹K-V。rocksdb只負責存儲kv
分片和redis同樣,1024個固定,每一個集羣管理部分
遷移:rocksdb文件快照,內存快照。slot遷移,增量記錄。當遷移結束直接返回false換路由(最後一個增量時間在一個qps就能夠),增量後merge.
複製:用rocksdb掃描key+多線程發送,同步成功點記錄
同步:WAL。採起同步成功刪除的方式。同步後用redis命令放入slot中
主備切換:codis的proxy感知切換
proxy用zk保證一致性
主備一致性WAL同步保證
詳細:https://segmentfault.com/a/11...
SSD+SST。在正常的讀寫性能寫入14M/S(32核),寫比讀稍好
ACID。
原子:WAL
隔離:版本快,常規的讀只會讀sequenceid以前的,內存中內容用sid控制,文件中version來組織,每次sid引用的version。
leveldb沒有任何分佈式。bigtable是chubby(分佈式鎖)+單機lebeldb。
rocksdb提供了基本的備份,增量備份,恢復,同步,事務日誌,兩階段提交的接口支持。能夠本身搭建proxy
wiredTiger引擎
詳細:https://segmentfault.com/a/11...
B樹,buffer,文檔(磁盤)
修改操做在持久化時在新頁中,不會對舊頁有影響,成塊寫入不須要二次寫
比較佔內存(一個鏈接一個線程,tcp鏈接500個就1G,引擎數據cache,新寫數據,備節點差別buffer,長事務快照,誇多集合時的排序)
運行模型:每一個鏈接一個線程,限制棧1M。雖然線程多切換代價大,但後臺都是IO操做,代價還好。請求調用引擎層的方法
K-V
單機AD WAL(journal)+checkpoint
CI 未提交事務快照(同mysql,只有讀用,寫仍是要最新的頁)
全量同步+oplog增量同步
主從同步:oplog 冪等(incr會轉爲set),循環覆蓋,
順序保證:寫入 oplog前,會先加鎖給 oplog 分配時間戳,並註冊到未提交列表裏,正式寫入 oplog,在寫完後,將對應的 oplog 從未提交列表裏移除,在拉取 oplog 時若未提交列表爲空,全部 oplog 均可讀,不然只能到未提交列表最小值之前的 oplog
Secondary 拉取到一批 oplog 後,在重放這批 oplog 時,會加一個特殊的 Lock::ParallelBatchWriterMode 的鎖,這個鎖會阻塞全部的讀請求,直到這批 oplog 重放完成
存儲/讀寫qps
集合分片:分片範圍,hash,tag(機房)
hash能夠預先分配多個。同一時刻搶鎖以後又一個mongos會遷移。遷移期間原請求阻塞排隊,返回給mongos從新請求
複製集模式:故障檢測恢復。成員間心跳,選舉primary(Bully算法)。driver與複製集合心跳
原數據的存儲介紹了工具,介質,結構等。實際上系統中還包括對源數據處理,好比緩存,計算。計算涉及到分佈式要並行處理,流數據計算等。這一章節說下分佈式下數據的處理
並行執行SQL。缺點:僅支持sql,傾向於內存中保存儘可能多數據,最多分鐘級別。分佈式文件系統的map-reduce或其餘優化計算方式,數據多樣性,不只關係或文檔;查詢不限於SQL(HIVE在此之上封裝了SQL);文件系統能夠包含MPP風格的或OLTP如HBase
unix管道處理的思想,sort內存溢出放入磁盤,輸入輸出與邏輯分離/統一接口/可複用 =》map/reduce
輸入輸出爲分佈式上的文件HDFS
好處:數據網絡傳輸和計算分離。永遠數據完備後才執行mapper或reducer;重試,失敗回滾都中間態存儲。
針對數據傾斜,每次都要map數據+reduce邏輯處理有一些優化。好比
正常都在reduce鏈接。某些能夠在mapper鏈接優化,好比大數據與小數據的連接,將小數據廣播打到mapper內存,mapper和reduce分區相同時,mapper只須要單獨單個分區。若分區原本有序,能夠直接在能夠在此完成reduce的工做
舉例:mapper根據須要對文檔集合分區,reducer建立該分區的索引,並將索引你文件寫入分佈式文件系統。增量索引寫入新的段,並在後臺壓縮與合併。就不詳細說了。
基於這種處理思想,整個批處理的構建系統hadoop:
hdfs
是存儲系統(見https://segmentfault.com/a/11...)yarn
是資源管理(見https://segmentfault.com/a/11...)
hadoop的高級該工具hive
(見https://segmentfault.com/a/11...),能夠自動組裝多個mapreduce階段
構建推薦系統等,在線使用,mapreduce入單獨數據庫。好比HBase
(見:https://segmentfault.com/a/11...)
把整個工做流做爲單個做業處理。替代map/reduce用算子,算子之間的鏈接能夠有:記錄從新分區排序/分區/廣播鏈接。若中間態丟失,從先前中間態或原始數據從新計算。spark用彈性分佈式數據集抽象跟蹤數據的譜系,而flink對算子狀態存檔,容許在執行過程當中你那個遇到錯誤的算子。許多不用排序的能夠流水線方式執行。好比組合分區的map+reduce(groupby,sort等)做爲一個subtask,另外一個分區map+reduce(groupby,sort等)+reduce2(sink組合)爲subtask2。1與2並行,2的reduce2等1。在map-reduce模式下是map1,map2並行,reduce分爲很groupby1,groupby2,而後再map1,map2。再sort1,sort2。Spark
的技術理念是基於批來模擬流的計算。而Flink
則徹底相反,它採用的是基於流計算來模擬批計算。
詳細見:因爲批處理和流處理的總體劃分思路(對分佈式數據任務的拆分方式)是同樣的。所以批處理和流處理的spark,flink寫在一塊兒了,詳細見:spark(https://segmentfault.com/a/11...、flink(https://segmentfault.com/a/11...
對分佈式數據的處理方法和上面同樣,只說下流裏邊特別的
上述基於數據有界=》數據無界,增量處理。批處理輸入是數據文件,須要考慮流處理的存儲和傳遞?上述的分佈式文件系統再也不行:增量要輪詢,輪詢的越頻繁,能返回新事件的請求比例就越低,而額外開銷也就越高。 相比之下,最好能在新事件出現時直接通知消費者(數據庫的觸發器功能有限)=》
設計點:費速度跟不上?丟棄,緩衝,背壓。節點故障?
1.1 直接發送:UDP組播,ZeroMQ
(https://segmentfault.com/a/11...),HTTP或者RPC(webhooks,一種服務器的url被註冊到另外一個服務中)。容錯能力有限
1.2 消息代理:負載均衡、扇出,併發確認重傳
1)RabbitMQ
,代理將單調消息分配給消費者,確認後刪除(https://segmentfault.com/a/11...
2)基於日誌的消息代理:使用日誌消息存儲。kafka
。每一個分區有序,每一個消息單調遞增偏移量,要記錄消費取的偏移量(https://segmentfault.com/a/11...)
適用於消息吞吐量高,處理迅速,順序很重要(能夠單分區全分能給某個負載均衡的線程)
數據庫/緩存/索引/數據倉庫
雙寫:1.兩個客戶端兩個系統相互覆蓋=》版本向量檢測併發寫入;2.一個成功一個失敗(原子)
選擇一個爲領導者,好比數據庫,讓其餘系統做爲追隨者。
變動數據捕獲,將其提取並替換爲能夠複製到其餘系統中的形式的過程 事件溯源,事件僅追加。日誌壓縮僅保存最新版本。通常的刪除都是使數據不能取回.能夠鏈接數據庫和衍生數據,使其做爲主。 可是解決了覆蓋但由於異步,還會有原子問題,使用分佈式事務
1.事件事件仍是處理時間?
處理時間:處理有問題,重啓後處理大量堆積的
事件時間:不知道1分鐘內的最後到達會在幾分鐘後=》丟棄或發佈更正
若要準確的事件時間(即事件在設備上發起時間,記錄該時間,發往服務器時間,服務器收到時間),經過從第三個時間戳中減去第二個時間戳,能夠估算設備時鐘和服務器時鐘之間的偏移(假設網絡延遲與所需的時間戳精度相比可忽略不計)。而後能夠將該偏移應用於事件時間戳,從而估計事件實際發生的真實時間(假設設備時鐘偏移在事件發生時與送往服務器之間沒有變化)。在spark中會介紹google基於水位線的事件發生事件
2.窗口
滾動窗口,按分鐘,每一個事件僅屬於一個窗口。
跳動窗口,有重疊固定。
滑動窗口,5分鐘內任意時間開始。
會話窗口,無會話關閉
3.容錯
沒法等待任務完成後根據輸出錯誤處理=》微批量spark,存檔點flink
詳細見spark(https://segmentfault.com/a/11...
flink(https://segmentfault.com/a/11...