Hbase是一個強一致性數據庫,不是「最終一致性」數據庫,官網給出的介紹:html
「Strongly consistent reads/writes: HBase is not an "eventually consistent" DataStore. This makes it very suitable for tasks such as high-speed counter aggregation.」java
這裏要先提一下分佈式系統的CAP原理:
Consistency(一致性), 數據一致更新,全部數據變更都是同步的
Availability(可用性), 好的響應性能
Partition tolerance(分區容錯性) 可靠性數據庫
定理:任何分佈式系統只可同時知足二點,無法三者兼顧。
忠告:架構師不要將精力浪費在如何設計能知足三者的完美分佈式系統,而是應該進行取捨。服務器
對於一致性,能夠分爲從客戶端和服務端兩個不一樣的視角。網絡
從客戶端來看,一致性主要指的是多併發訪問時更新過的數據如何獲取的問題。從服務端來看,則是更新如何複製分佈到整個系統,以保證數據最終一致。一致性是由於有併發讀寫纔有的問題,所以在理解一致性的問題時,必定要注意結合考慮併發讀寫的場景。架構
從客戶端角度,多進程併發訪問時,更新過的數據在不一樣進程如何獲取的不一樣策略,決定了不一樣的一致性。對於關係型數據庫,要求更新過的數據能被後續的訪問都能看到,這是強一致性。若是能容忍後續的部分或者所有訪問不到,則是弱一致性。若是通過一段時間後要求能訪問到更新後的數據,則是最終一致性併發
從服務端角度,如何儘快將更新後的數據分佈到整個系統,下降達到最終一致性的時間窗口,是提升系統的可用度和用戶體驗很是重要的方面。對於分佈式數據系統:異步
若是W+R>N,寫的節點和讀的節點重疊,則是強一致性。例如對於典型的一主一備同步複製的關係型數據庫,N=2,W=2,R=1,則無論讀的是主庫仍是備庫的數據,都是一致的。分佈式
若是W+R<=N,則是弱一致性。例如對於一主一備異步複製的關係型數據庫,N=2,W=1,R=1,則若是讀的是備庫,就可能沒法讀取主庫已經更新過的數據,因此是弱一致性。性能
對於分佈式系統,爲了保證高可用性,通常設置N>=3。不一樣的N,W,R組合,是在可用性和一致性之間取一個平衡,以適應不一樣的應用場景。
Hbase具備如下特色
聯繫上文提到的一致性特色,能夠得出HBase是強一致性系統的結論。
當某臺region server fail的時候,它管理的region failover到其餘region server時,須要根據WAL log(Write-Ahead Logging)來redo(redolog,有一種日誌文件叫作重作日誌文件),這時候進行redo的region應該是unavailable的,因此hbase下降了可用性,提升了一致性。設想一下,若是redo的region可以響應請求,那麼可用性提升了,則必然返回不一致的數據(由於redo可能還沒完成),那麼hbase就下降一致性來提升可用性了。
一開始很是迷惑於HBase的強一致性和HDFS的多副本是怎麼協同的。
這一起就須要對HBase和HDFS的讀寫數據流有個比較透徹的理解。
先假設HDFS的副本存儲策略,也就是dfs.replication的值爲3(默認值就是3)
這樣全部存儲在HDFS上的文件都有3個副本。那麼,HBase的存儲實例,也就是HFile也有3個副本。那麼當某一個RegionServer崩潰時,並不用擔憂數據的丟失,由於數據是存儲在HDFS上,哪怕崩潰的RegionServer所在的DataNode上有一個副本,在其餘DataNode上也還有2個副本。
那麼也許你要問,既然有3個副本,如何保證HBase的強一致性呢?
HFile是已經持久化在磁盤上了,而HFile是不能改變的(這個時候暫時把刪除數據這個操做放到一邊,相關內容請看下面的Note),一旦在某一個DataNode上生成一個HFile後就會異步更新到其餘兩個DataNode上,這3個HFile是如出一轍的。
那也許你又要問,那個人數據是不斷更新當中啊!
更新的數據是放在Memstore,只有當Memstore裏的數據達到閾值,或者時間達到閾值,就會flush到磁盤上,生成HFile,而一旦生成HFile就是不可改變的(compaction,split就是後話啦)。
這裏再提一下WAL的一致性
WAL是Write-Ahead logging,這個是Memstore裏的數據在RegionServer崩潰時得以恢復的保證。WAL的實現是HLog,HLog也是存儲在HDFS上的,因此HRegionServer崩潰了也不會致使HLog的丟失,它也有備份。
每一次更新都會調用寫日誌的sync()方法,這個調用強迫寫入日誌的更新都會被文件系統確認。
當前的sync()的實現是管道寫,也就是HDFS寫數據、生成副本的默認方式,這意味着當修改被寫入時,它會被髮送到第一個DataNode進行存儲。一旦成功,第一個DataNode就會把修改發送到另外一個DataNode來進行相同的工做。只有3個DataNode都已經確認了寫操做,客戶端才被容許繼續進行; 另外一種存儲修改的方法是多路寫,也就是寫入被同時送到3臺機器上。當全部主機確認了寫操做後,客戶端才能夠繼續。 兩種方法的優缺點: 管道寫須要時間去完成,因此它有很高的延遲,可是它能更好地利用網絡帶寬;多路寫有着比較低的延遲,由於客戶端只須要等待最慢的DataNode確認(假設其他都已成功確認)。可是寫入須要共享發送服務器的網絡帶寬,這對於有着很高負載的系統來講是一個瓶頸。 目前有正在進行的工做能讓HDFS支持上面兩種方式。 |
Note:當客戶端提交刪除操做的時候,數據不是真正的刪除,只是作了一個刪除標記(delete marker,又稱母被標記),代表給定航已經被傷處了,在檢索過程當中,這些刪除標記掩蓋了實際值,客戶端讀不到實際值。直到發生compaction的時候數據纔會真正被刪除。
參考文獻
【1】http://kabike.iteye.com/blog/2168852?utm_source=tuicool
【2】http://www.blogjava.net/hello-yun/archive/2012/04/27/376744.html
【3】《HBase權威指南》Lars George著