【2.綜述數據存儲/計算】【精】

實在不知道起什麼名字,打算寫三個系列的文章,由於系統儘可能有狀態和無狀態分離,第一個想寫下系統中涉及到數據存儲/分析/選型/本身設計要怎麼作,包含內存/持久,數據量單機到大量到海量分佈式的存儲和計算。第二個想寫無狀態系統如何作到高處理能力,涉及到網絡/分流/通訊,進程/線程模型,操做系統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

數據組織

  • 模型
    1.SQL 對象和關係不批配,ORM不能隱藏,支持XML/JSON轉爲行或者列
    2.文檔型,多對1關係,文檔對鏈接支弱,多對多=》文檔引用(並不是全部文檔性都支持mongodb不支持鏈接)。兩者對於複雜關係都拋棄了舊的網絡模型(網絡模型必須搜索全部路徑)
    文檔讀取只能讀取一整條,沒法直接獲取第二項;使用文檔的好處是:文檔(即對象)對應於許多編程語言中的本機數據類型。嵌入式文檔和數組減小了對昂貴鏈接的需求,//減小join的IO。動態模式支持流暢的多態性。
    3.圖模型:當多對多特別頻繁,社交圖譜,網絡圖譜等。三元組存儲。SPARQL
    與網絡差異:無模式,任意記錄類型的嵌套,經過惟一ID直接飲用任何節點,也能夠創建索引查詢定點,網絡中只能經過範耐高溫你路徑,訂點和邊無序,查詢支持SPARQL
  • 索引及數據存儲方式
    1.文件追加加入+hash索引:
    hash索引key和偏移量:散列表必需要內存中放得下,不然須要大量隨機訪問,不支持範圍查詢
    分段,開啓新段,壓縮合並舊段。逐個向後找。舊段的索引、舊數據的刪除能夠經過偏移量最小點刪除或者保持冗餘
    考慮點:刪除標記,崩潰hash索引的恢復,數據庫中途崩潰,併發控制

    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

  • 分析=》大量掃描索引不重要了,解決IO問題。壓縮
    上面的模型和結構在事務中很常見,在分析系統中,有些經常使用過簡單的模型和索引等
    星型和雪花型
    列存儲:行巨多但每次只查詢少數列。=》列壓縮,避免修改的LSM
  • 數據格式
    內存中:針對cpu和操做優化,樹、列表
    文件/網絡傳輸:編碼,json/xml/csv/二進制(thrift,protocal buffer)
    須要考慮:數據表更改兼容(數據庫中的數據,REST/RPC/Mq中的數據)
    這部分詳見:https://segmentfault.com/a/11...

功能

功能各個系統本身針對不一樣會有很大差異(排序,聚合,搜索等),通用的功能只有事務,這裏作下討論,其餘特性能夠查API,功能和使用通常文檔都比較好查,不仔細說了redis

事務

  • 單對象,日誌崩潰恢復和回滾實現原子性+鎖實現隔離性+CAS實現複雜的自增原子操做,可是事務更多指多對象
  • 原子性保證:停止後能夠安全重試。無主複製的數據存儲盡力而爲遇到錯誤不撤銷
    但重試還可能的問題:
    若是服務器已經成功但返回超時,可能成功兩次
    錯誤因爲負載過大形成,重試會形成更大的
    僅在臨時性錯誤後才值得重試,永久性錯誤重試沒沒有意義
    事務後還反作用,好比發送郵件,幾個系統一塊兒,二階段提交
  • 隔離
    1.髒寫:行寫鎖
    2.髒讀:鎖代價大=》持有寫入鎖能夠設置新值,讀舊數據。讀已提交的隔離級別,只須要保留一個版本
    3.可重複讀:A事務過程當中讀取數據是一致的,即便該數據在期間被B修改,實現:MVCC。讀已提交爲每一個查詢使用單獨的快照,可重複讀對整個事務使用相同的快照。記錄建立和刪除的事務號,對大事務號的寫入操做都忽略。索引能夠指向全部版本,再過濾,能夠全部版本存儲在一個節點。還有僅追加,不更新,建立新的樹分支,不須要再寫另外存儲(mongodb),mysql則在回滾段中恢復。
    4.關於幻讀:網上的資料都是錯的,MVCC能夠保證讀的正確性,會找舊版本(mysql是undo恢復)。可是對寫不保證,由於寫會取最新的,只能用鎖保證,好比雖然事務中select * 取不到另外一個事務的,可是Insert可能會衝突報錯,select * for update也會讀到B事務的,由於讀取是最新的。若要保證更新沒問題用select for update加鎖(select * for update讀最新加間隙鎖)。這種先select檢查再update的行爲致使會議室預約等問題。幻讀實際上是寫誤差。當能夠間隙所時加鎖,不能好比!=1
    5.丟失更新:A select value=2後update value=value+1。可是期間B事務insert value=3。此時value=4都是由於寫都是最新的。這種問題mysql可重複讀不保證,要麼數據庫加排它鎖保證整個事務原子,要麼顯示鎖定好比for update,代價較大。或者CAS,update value=value+1 where value=3;
    分佈式丟失更新?容許多節點併發,不能用鎖和CAS=>衝突解決
    6.串行化:單線程(擴展性很差,分佈式要一個一個等,爲了提升性能能夠一個分區一個CPU一個事務線程,對於誇多分區的須要分區鎖,尤爲二級索引,吞吐量差)
    7.鎖
    兩階段鎖定:讀共享鎖寫升級爲排他鎖,寫要等全部的讀釋放,讀必須等寫釋放,常常發生死鎖。鎖粒度控制:謂詞鎖(性能很差,符合搜索條件的再加鎖)=》間隙鎖(範圍或全表)
    SSI 序列化快照隔離:悲觀鎖=》樂觀鎖(先執行到提交時再檢查是否與隔離違背,違背則停止重試)。快照隔離+檢測寫入序列化衝突。(讀以前存在未提交的寫入)MVCC的讀在提交時檢查是否有被忽略的寫入已經被提交。寫入數據時通知全部最近讀取的其餘事務(讀以後發生寫入)

擴展性

  • 分片
    hash(事先分多)
    hash+鍵值組合
    一致性hash(不推薦,寫很差)
    range(hbase 熱點問題,分配不均,掃描和範圍查詢)
  • 二級索引:
    1.按主鍵ID範圍分區,寫入本地分區二級索引,合併讀取。好比a的color放a機,按color查須要合併a和b
    2.全局按關鍵詞索引,索引自己分佈式。color自身分佈式,每次寫入a要寫更新a和b機器的color索引,寫慢讀快。由於跨分區事務問題和通常異步更新二級索引,二級索引有不一樣步問題(不一致或部分更新失敗,我認爲分別是一致性問題和事務原子性問題)
  • 分區再平衡:
    1.固定分區數:若是用hash%n的形式(須要移動所有數據),須要提早分配更多,按照子集擴展,好比。雖然只有4個節點,開始就設置N爲20
    2.動態分區數,按數據範圍,相似B樹節點的合併和拆分。mongodb的範圍和hash都是動態分球
    3.固定節點數。每一個節點有n個分區。新加入節點隨機獲取幾個舊節點中幾個的分區的一半組成新的分區,剩餘的留在原來分區中,增長節點會增長分區數量,爲了均衡隨機選擇分區拆分,要求分區邊界基於散列的分區。(最符合一致性hash的語義)。好比卡桑德拉https://issues.apache.org/jir...,一致性hash基礎上,每一個節點256個虛擬節點,配置範圍,新增節點將其餘節點的部分範圍分給新的vnodes(好比256分82%~102%)
    4.CRUSH這種(3須要記錄具體配置,或者其餘負載均衡都須要記錄對應配置)不須要記錄配置,hash到虛擬gid上個,gid與節點id一塊兒產生隨機數,全部節點id中選擇隨機數*權重最大的。隨機數是僞隨機,固定的,新增只須要把更大的移走,減小隻須要移動到次大的。
  • 元數據部署
    路由決策組件:節點,單獨路由,客戶端
  • 分區並行查詢問題後續再說

可靠性

單獨說可靠性指異常補償,涉及到複製,故障發現,故障自動轉移。比較簡單。有些副本提供讀服務還有多寫副原本提高可用性,有時存儲的可靠性和可用性能夠一塊兒解決,簡單說下這種部署和延時問題,詳細的一致性在下面說。算法

  • 目的:考慮高可用的備份,高性能的負載,地理就近進行數據複製,通常三個副本能夠保證11個9,兩副本大規模下3個9必丟數據
  • 運行:同步,異步(絕大多數領導者形式都是這個),半同步:一個追隨者同步,其餘異步,至少有兩個節點擁有最新的數據副本
  • 擴容:設置新從庫,從主庫拉取歷史快照,從主庫讀取落後新數據直到commentid一致(須要log)
  • 故障發現,主動上報/心跳/lease
  • 故障恢復:
    1.從庫,日誌點
    2.主庫:確認主庫失效(須要考慮超時設置多少合理?)
    選擇新主庫 (腦裂=》單網絡通道,奇數,仲裁/搶鎖通道)
    更新客戶端和其餘從庫配置
    舊主庫恢復數據衝突處理
  • 複製方案:
    基於語句的。不肯定性處理rand().now(),自增(mysql5.1之前),緊湊
    基於物理的。用於主存儲或崩潰恢復。記錄數據底層磁盤塊字節更改,與存儲緊密耦合,存儲格式更改版本,若複製協議不支持匹配版本,須要停機處理
    邏輯日誌,行復制。更新:惟一標識+全部列新值
    觸發器(只複製部分)
  • 複製延遲:幾種保證:讀寫一致性(本身更新本身馬上可看),單調讀(同一用戶屢次讀取一致),一致性前綴讀(保證讀取順序按寫入順序),最終一致性。
    前三個一致性解決方法舉例:更多內容見一致性。
    1)讀主庫,剛更新數據讀主庫,記錄時間戳;本身請求讀主庫,注意不一樣網絡和設備,分佈打到多個數據中心的須要路由到主庫的數據中心
    2)同一用戶讀固定從庫,防止更舊
    3)依賴事務有因果關係的寫入 同一個分片。不一樣分片不保證順序一致
  • 部署:
    1.單主
    2.多主,只有多活多數據中心用到。或者須要每一個本地離線寫入。

    clipboard.png
    多活多數據中心與單活對比:單活每一個寫入都要進入主庫數據中心,增長寫入時間;經過網絡中心的寫是同步的,對網絡容忍度比多中心異步複製低不少;故障一個主從切換,一個換主。
    處理寫入衝突:寫入檢測兩個主違背多主目的,寫入都成功後同步時檢測衝突用戶沒法補救。
    併發寫入:哪些須要覆蓋(好比依賴關係的B知道A先發生,只須要覆蓋便可),哪些是併發(B不知道A的發生,併發須要按版本解決或者容許丟失用時間或序號。)
    =》
    預防衝突:同一寫入只入一個,在故障等時特殊處理(須要切換,保證不了同一個請求不丟失或不重複)。
    衝突合併:每一個寫入或副本一個惟一ID,a.覆蓋丟棄,b.維護多版本,自動處理的數據類型:集合等,帶多版本等。
    b.版本向量:返回時將讀取自的版本返回,再次寫入時帶該版本的就是依賴(原本知道有這個版本),可覆;不然是併發保留保本(原本不知道有這個版本)。sql

    clipboard.png

    3.無主:所有多讀多寫,返回數量足夠成功。(讀寫法定人數的確認)
    單個落後如何恢復:讀取時選擇版本高的反更新(讀修復)或 異步不斷同步差別(反熵,不保證順序)
    寫入衝突和多主同樣mongodb

一致性

困難:丟包\延遲,時鐘不一樣步。會致使設計上再延時(同步,半同步,異步)和一致性(線性一致性,原子事務提交)上有必定取捨,現實中模型:同步模型,假設網絡延遲,進程暫停,時鐘偏差都有界限;部分同步:大多數同步,偶爾變得至關大;異步模型:不能用超時,沒有時鐘。崩潰-中止故障:中止後永遠不回來\崩潰恢復故障:未知時間後再次開始響應,節點具備穩定的存儲且會保留/內存會丟失。拜占庭故障:包括試圖戲弄和欺騙其餘節點。能夠實現不一樣等級的一致性:如ACID,最終一致性,sessioin一致性,單調一致性
CAP是針對一條數據的(同一個系統數據可根據要求作不一樣處理)。一致性的定義應該是普遍的(不僅是副本之間數據相同),能夠理解爲對一條數據獲取的一致性,多我的多同一條數據讀結果一致,一個事務對同一數據讀結果一致,惟一性讀取一致(惟一被獲取其餘讀應該失敗)
寫入順序與實際一致 不屬於一致性範疇但應儘可能保證才使得讀有意義,也歸到這裏討論。叫因果一致性
最強的一致性是線性一致性(一旦寫入成功讀取的就是該值,直到再次覆蓋)
所以涉及到問題大概有:因果一致性保證,線性一致性保證,事務一致性保證,惟一性約束
  • 因果一致性
    lamport時間戳,一個客戶端在多個寫節點中的順序保證,多節點寫入時保證同步時因果覆蓋正確。解決同一客戶端發出對同一操做兩個有序請求,最終到兩個主庫上,序號(到達是有序的,但時鐘不可靠)不必定哪一個在後,因此同步時有錯的問題。辦法就是每次請求帶節點最大序號,更新落後的節點。(不需去取全局遞增序號)
    在紙上畫了一下,與其餘方案對比(不一樣寫入按時間戳,分奇數偶數都會使得最終多副本順序和真實一個客戶端寫入順序不一樣)。不方便畫。注意和上面多版本不要緊呦,一個是單數據庫控制多版本並存解決衝突,一個是對同一cli的多數據庫寫提供順序sid保證安全覆蓋,不一樣cli仍是要兩個version(知道因果的有序)

    只是提供同步序號問題,只有同步解決纔會獲得全局數據,讀數據調的應用需阻塞在全局回覆上數據庫

  • 全序廣播
    順序在消息傳遞時被固化,不容許將消息插入到順序中較早位置。全序廣播保證以固定順序可靠的發送,不保證消息什麼時候被送達.
    全序廣播實現線性一致性存儲apache

    向全序日誌中追加一個惟一的用戶名,讀日誌,等待消息被送回時執行。用一個全序日誌作同步(C讀,A讀,B寫,A寫、每一個寫所有節點都執行,讀要等寫都執行完,是本身調的返回客戶端)
    寫一致性:若該日誌第一條消息全部權是本身的,確認提交(給客戶端返回)。如有併發寫入,全部節點會對最早到達者達成一致。非本身節點也執行的
    讀取一致性:當有讀取時追加一條消息,消息回送時讀取日誌,執行實際的讀取返回。用全序控制線性,串行。
  • 線性一致性:包含因果性(從概念上是讀取最新的寫入,可是寫入有延遲是可接受的,讀取不變便可)
    線性一致性是新鮮性的保證,讀取必定能看見最新的寫入值
    線性一致性存儲實現全序廣播
    每次全序廣播發送首先從線性一致執行原子自增返回(好比寄存器執行自增並返回)操做,做爲序號附加到消息中,將消息發送到全部節點,收件人按照序號連續處理消息。與lamport時間戳不同,一致性存儲數字沒有間隙,若是節點發送了4而且收到6,在傳遞6以前必須等5再發送6。
    全序廣播和線性一致都等價於共識
  • 分佈式事務原子提交
    意義:在客戶端或者proxy或者C贊成提交後掛掉(不考慮回來,回來補償)的狀況下,讓分佈式的提交原子性,要麼不commit,一個commit就全commit
    前提:2pc,3pc都是要一致(崩潰是最終一致),CL之間不通訊。CL崩潰不恢復(恢復能夠單獨用日誌,即保證在線節點一致便可)。一旦commit沒法rollback
    對應mysql語句:update1 end1 prepare1;update2 end2 prepare2; commit1->commit2/rollback1->rollback2
    1.二階段提交

    clipboard.png

    clipboard.png
    第一階段:收集協商,若參與者返回OK,即保證undo落盤可提交可回滾了。第二階段把redo落盤或者undo回滾
    若是協調者在準備好後失敗,不得不等待他從新恢復。協調者上的常規單點提交。協調者有問題,鎖沒法釋放棘手問題.即便協調者從新第二階段沒法判斷是否是要提交,1在線可是隻能知道本身yes,若2失敗沒法獲取2的投票和狀態。
    缺陷:協調者單點,引入協調者可能使得服務器再也不是無狀態的不能隨意擴容,當誇各類數據系統時,須要時全部系統的最小集不能檢測系統間死鎖,沒法SSI等,數據庫內部的分佈式事務(其實非XA)沒有3問題可是系統任何部分失敗都會失敗 擴大失效=》改用共識編程

    2.三階段分佈式原子提交

    clipboard.png

    只要有一個進入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最終有收到有沒掛沒收到自動超時仍是有問題。
    對於網絡分區能夠用共識解決,恢復重啓須要同步日誌

  • 共識
    詳細見單獨:https://segmentfault.com/a/11...
    全序廣播至關於重複多倫共識。

常見數據存儲開源方案

1.redis、codis

性能

內存,多種內存數據結構

功能

無索引,基本不支持事務

redis的分佈式

1,代碼中寫;
2,redis Cluster。請求不在的key要兩次,先返回ip再請求一次
3,代理分片,好比tuemproxy,codis

redis cluster

1.主從模式。一主多從

  • 可靠性:沒法自動故障轉移
  • 無擴展性
  • 複製:BGSAVE命令生成一個RDB文件+緩衝區命令
  • 同步:
    寫命令傳播給從服務器
    每秒一次頻率向主服務器發送REPLCONF ACK <replication_offset>進行心跳檢測。檢測網絡和命令丟失。(主服務器配置min-slaves-to-write n, min-slaves-max-lag m當從服務器數量少於3個,或者延遲大於等於10將拒絕執行寫命令根據replication_offset檢測是否丟失命令,補發命令)
    斷點續傳:replication_offset,複製積壓緩衝區,服務運行ID

2.哨兵模式。哨兵系統也是一個或多個特殊的redis服務器,監視普通服務器,負責下線主服務器和故障轉移

  • 可靠性:自動故障轉移(哨兵經過raft協議選主,主哨兵選擇主服務器)。
    1.相互發現
    2.sentinel默認1s/次的頻率向全部主/從/sentinel服務器發送PING命令,有效回覆爲+PONG,-LOADING,-MASTERDOWN。當一個實例在down-after-milliseconds內,連續向sentinel返回無效回覆,sentinel修改實例中flags加入|SRI_S_DOWN標識主觀下線
    3.當接收到認爲下線的sentinel數量超過quorum則flags加|SRI_O_DOWN
    4.第一個標主觀的發起選主,成爲主哨兵
    5.故障轉移
    領頭進行故障轉移
    1) 選出新的主服務器
    在線的,5s內回覆過INFO的,與主服務器斷開鏈接時間足夠短,優先級高,複製偏移量大,runid最小的,發送SLAVEOF no one,以1s/次(其餘是10s/次)的頻率向該服務器發送INFO。當role變爲master時繼續2
    2) 向下線的主服務的其餘從服務器發送SLAVEOF命令
    3) 向舊的主服務器發送SLAVEOF命令
  • 無擴展性

3.集羣模式。去中心化,增長可擴展性,每一個能夠讀寫

  • 可靠性:
    gossip協議,主從自動故障轉移,gossip通訊,從節點發現故障,raft從新選主
  • 擴展性:16384個槽。以槽爲單位,從新分片
    指派槽與節點
    key與槽:CRC16(KEY) & 16383
    讀寫到任意節點, 二次轉移
    從新分片:redis-trib
  • 元數據:全部節點保存

codis

擴展性

分片: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...

2.mysql、proxy

性能

磁盤,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...

3.fusion

沒有開源。基於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同步保證

4.leveldb/rocksdb

詳細:https://segmentfault.com/a/11...

性能

SSD+SST。在正常的讀寫性能寫入14M/S(32核),寫比讀稍好

功能

ACID。
原子:WAL
隔離:版本快,常規的讀只會讀sequenceid以前的,內存中內容用sid控制,文件中version來組織,每次sid引用的version。

分佈式

leveldb沒有任何分佈式。bigtable是chubby(分佈式鎖)+單機lebeldb。
rocksdb提供了基本的備份,增量備份,恢復,同步,事務日誌,兩階段提交的接口支持。能夠本身搭建proxy

5.mongodb

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與複製集合心跳

一致性

  • 路由(客戶端,或mongos或單獨的connfig server)
  • 數據:oplog保證

第二部分 衍生數據的處理

原數據的存儲介紹了工具,介質,結構等。實際上系統中還包括對源數據處理,好比緩存,計算。計算涉及到分佈式要並行處理,流數據計算等。這一章節說下分佈式下數據的處理

批處理

1.MPP

並行執行SQL。缺點:僅支持sql,傾向於內存中保存儘可能多數據,最多分鐘級別。分佈式文件系統的map-reduce或其餘優化計算方式,數據多樣性,不只關係或文檔;查詢不限於SQL(HIVE在此之上封裝了SQL);文件系統能夠包含MPP風格的或OLTP如HBase

2.map/reduce

unix管道處理的思想,sort內存溢出放入磁盤,輸入輸出與邏輯分離/統一接口/可複用 =》map/reduce
輸入輸出爲分佈式上的文件HDFS

clipboard.png
好處:數據網絡傳輸和計算分離。永遠數據完備後才執行mapper或reducer;重試,失敗回滾都中間態存儲。
針對數據傾斜,每次都要map數據+reduce邏輯處理有一些優化。好比
正常都在reduce鏈接。某些能夠在mapper鏈接優化,好比大數據與小數據的連接,將小數據廣播打到mapper內存,mapper和reduce分區相同時,mapper只須要單獨單個分區。若分區原本有序,能夠直接在能夠在此完成reduce的工做
舉例:mapper根據須要對文檔集合分區,reducer建立該分區的索引,並將索引你文件寫入分佈式文件系統。增量索引寫入新的段,並在後臺壓縮與合併。就不詳細說了。

基於這種處理思想,整個批處理的構建系統hadoop:

clipboard.png
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...

3.spark/flink

把整個工做流做爲單個做業處理。替代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.1 直接發送:UDP組播,ZeroMQhttps://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...

應用

clipboard.png

  • 離線統計、數據量大(天級別)。kafka=>hive 自行查詢出報表,表小分鐘級別也出來了
  • 分鐘內級別統計。kafka=>HBASE
  • 實時簡單搜索

    clipboard.png

  • kafka=>ES(ES算是數據或衍生數據的主要提供索引外加一些聚合,單獨篇章詳見:https://segmentfault.com/a/11...
  • s級別實時報警: kafka=>spark/flink(要求更快能夠加druid)=>實時監控+預測模型
  • odin監控採集:採集,tsdb,druid。
相關文章
相關標籤/搜索