HBase服務端沒有提供update,delete接口,HBase中對數據的更新、刪除操作都認為是寫入操作,更新操作會寫入壹個最小版本數據,刪除操作寫寫入壹條標記為deleted的KV數據
1.1、寫入流程三個階段概況
1)客戶端處理階段:客戶端將用戶請求進行預處理,並根據集群元數據定位寫入數據所在的RegionServer,將請求發送給RS
2)Region寫入階段:RS收到請求之後解析數據,首先把數據寫入WAL,再寫入對應Region對應的MemStore
3)MemStore Flush階段:當Region中MemStore容量達到壹定閾值之後,系統異步執行flush操作,將內存寫入文件,形成HFile
1.2、用戶寫入請求在完成寫入MemStore之後就會返回成功。MemStore Flush是壹個異步執行的過程。
1.3、客戶端處理階段步驟詳解:
1)客戶端可以設置批量提交,如果設置了批量提交(autoflush=false)客戶端會先將數據寫入本地緩沖區等達到壹定閾值之後才會提交。否則put請求直接會提交給服務端進行處理。
2)RS尋址,在提交之前HBase會在元數據表hbase:meta中根據rowkey找到她們歸屬的RS
2.1)客戶端根據寫入的表和rowkey在元數據中查找,如果能夠查找出該rowkey所在的RS及Region,就直接發送寫入請求
2.2)如果客戶端沒有找到rowkey信息,需要首先到zk上找到hbase:meta表所在的RS,向那RS發送查詢請求獲取元數據,然後在元數據中查找rowkey所在的RS,並將元數據緩存在本地,以備下次使用。
3)客戶端發送遠程RPC請求給RS,將數據寫入目標Region的MemStore中
1.4、Region寫入階段步驟詳解:
1)獲取行鎖,HBase中使用行鎖保證對同壹行數據的更新是互斥操作,用以保證更新的原子性,要麽成功要麽失敗
2)更新所有待寫入keyValue的時間戳為當前系統時間
3)對壹次寫入同壹個Region的壹個或多個KeyValue構建壹條WALEdit記錄,這樣做的目的是保證Region級別事務的寫入原子性
4)把WALEdit寫入HLog,HLog是存儲在HDFS上需要sync操作把HLog真正落地到HDFS,在這壹部暫時不用執行sync,HBase使用了disruptor實現了高效的生產者消費者隊列,來異步實現WAL的追加寫入操縱
5)寫入WAL之後再將數據寫入MemStore
6)釋放行鎖
7)sync WAL:將HLog真正sync到HDFS,如果sync失敗,執行回滾操作將MemStore數據移除
8)結束寫事務。更新對外可見,更新生效
1.5、MemStore Flush階段詳解:
1.5.1、觸發flush條件
1.5.1.1、MemStore級別限制,當Rgion中任意壹個MemStore大小達到閾值(hbase.hrgion.memstore.flush.size)默認128M
1.5.1.2、Region級別限制:當Region所有MemStore的大小達到了上限(hbase.hregion.memstore.block.multiplier * hbase.hrgion.memstore.flush.size)超過memstore大小的倍數達到該值則阻塞所有寫入請求進行flush,自我保護默認是2.
1.5.1.3、RegionServer級別限制:當RS中MemStore的總大小超過低水位閾值hbase.regionserver.global.memstore.size.lower.limit * hbase.reagionserver.global.memstore.size RS則開始強制執行flush,按Region中MemStore大小從大到小進行flush,直到總MemStore大小下降到低水位。
1.5.1.4、當壹個RegionServer中HLog數量達到壹定上限(hbase.regionserver.maxlogs),系統選擇最早的HLog對應的Rgion進行Flush
1.5.1.5、HBase定期Flush,默認是1小時確保MemStore不會長時間沒有持久化。為了避免同壹時間所有都進行flush,定期的flush操作有壹定時間的隨機延遲
1.5.1.6、手動flush,用戶可以通過flush 'tablename'或者 flush 'regionname'對壹個表或者Region進行flush
1.5.2、flush執行步驟
1.5.2.1、prepare階段
遍歷當前region下的MemStore做壹個快照,然後新壹個ConcurrentSkipListMap接受新的數據請求。此階段需要通過鎖來阻塞寫請求,結束後釋放鎖,此過程持鎖時間很短
1.5.2.2、flush階段
對快照數據按照特定格式生成HFile持久化為臨時文件放在.tmp目錄下。這個過程涉及到磁盤IO操作,相對比較耗時
1.5.2.3、commit階段
把臨時文件移動到指定的CF目錄下。再清空快照數據。
1.5.3、MemStore Flush對業務的影響
1.5.3.1、大部分MemStore Flush操作都不會對業務讀寫產生太大影響,
1.5.3.2、Region Server級別呆滯的flush,會對用戶請求產生較大影響,會阻塞落在該RS上的寫入操作。
1.6、HLog寫入模型
1.6.1、HLog持久化級別
SKIP_WAL:只寫緩存,不寫HLog,不可取
ASYNC_WAL:異步寫入HLog
SYNC_WAL:同步寫入日誌文件,數據只是被寫入文件系統緩存中並沒有真正落盤。默認是此級別
FSYNC_WAL:同步將數據寫入日誌文件並強制落盤,這是最嚴格的寫入級別,保證數據不丟失,性能相對較差
USER_DEFAULT:如果用戶沒有指定持久化級別,默認HBase使用SYN_WAL等級持久化數據put.setDurability(Durability.SYNC_WAL);
1.6.2、HLog寫入模型
1、HLog寫入需要經過3個階段:手寫將數據寫入本地緩存,然後將本地緩存寫入文件系統,最後執行syn操作同步到磁盤
2、HBase使用LMAX Disruptor框架實現了無鎖有界隊列操作,寫入模型如下圖
2、BulkLoad 流程
2.1、BulkLoad使用場景:用戶數據位於HDFS中,業務需要定期將這部分海量數據導入HBase系統.
2.2、核心流程分兩步
2.2.1、HFile生成階段:運行壹個MapReduce任務,map需要自己實現,將HDFS文件中的數據讀取出來組裝壹個復合KV,其中Key是rowkey,Value可以是KeyValue對象、Put對象甚至Delete對象;reduce由HBase負責,他會根據表信息配置壹個全局有序的partitioner,將partitioner文件上傳到HDFS集群,設置reduce task個數為目標表的Region個數。為每個Region生成壹個對應的HFile文件
2.2.2、HFile導入階段:HFile主備就緒後,將HFile加載到在線集群。
2.3、Bulkload遇到的壹些常見問題
2.3.1、設置正確的權限
2.3.1、BulkLoad操作過程涉及到的用戶:
第壹步,通過MapReduce任務生成HFile。假設這個過程使用的HDFS賬號為:u_mapreduce.
第二步,將HFile加載到HBase集群,假設這個步驟使用的賬號為:u_load。
壹般地:HBase集群由壹個專門的賬號用來管理HBase數據,該賬號擁有HBase集群的所有表的最高權限,
同時可以讀寫HBase root目錄下的所有文件,假設這個賬號為:hbase_srv
2.3.2、權限設置
2.3.2.1、通過MapReduce任務生成HFile,HFile文件的owner為u_mapreduce。
2.3.2.2、u_load需要HFile文件以及目錄的讀、寫權限。寫的權限是因為在HFile跨越多個Region時,需要對HFile進行split操作。
另外u_load賬號需要HBase表的Create權限
2.3.2.3、hbase_srv賬號把HFile文件從用戶的數據目錄rename到HBase的數據目錄,所以hbase_sHrv需要有用戶數據目錄及HFile的讀取
權限,但事實上僅讀取權限還不夠,應為加載到HBase數據目錄的HFile目錄的owner仍為u_mapreduce。壹旦執行完compaction操作
之後,這些文件無法挪動到archive目錄,導致文件越來越多。這個問題在HBase 2.x 上修復。
2.3.2、影響Locality
如果生成HFile都在的HDFS集群和HBase所在HDFS集群時同壹個,則MapReduce生成HFile,能夠保證HFile與目標Region落在同壹個機器上。這樣就保證了Locality。由hbase.bulkload.locality.sensitive.enabled的參數控制整個邏輯,默認是true.所以默認保證locality的。
如果用戶MapReduce在A集群上生成HFile,通過distcp拷貝到集群B.這樣BulkLoad到HBase集群數據是沒法保證Locality的。需要跑完BulkLoad之後再手動執行major compact,來提升loaclity。
2.3.3、BulkLoad數據復制
在1.3之前版本中,BulkLoad到HBase集群的數據並不會復制到備集群,這樣可能無意識的導致備集群比主集群少了很多數據。在HBase1.3版本之後開始支持BulkLoad數據復制。需要開啟開關:hbase.replicatition.bulkload.enabled=true。