hbase存儲:HBase存儲數據其底層使用的是HDFS來做爲存儲介質,HBase的每一張表對應的HDFS目錄上的一個文件夾,文件夾名以HBase表進行命名(若是沒有使用命名空間,則默認在default目錄下),在表文件夾下存放在若干個Region命名的文件夾,Region文件夾中的每一個列簇也是用文件夾進行存儲的,每一個列簇中存儲就是實際的數據,以HFile的形式存在。路徑格式以下:算法
/hbase/data/default/<tbl_name>/<region_id>/<cf>/<hfile_id>
namespace:命名空間的做用是把多個屬於相同業務領域的表分紅一個組。一個表能夠自由選擇是否有命名空間,若是建立表的時候加上了命名空間後,這個表名字就成爲了:<NameSpace> : <Table>。經過命名空間咱們能夠像關係型數據庫同樣將表分組,對於不一樣的組進行不一樣的環境設定,好比配額管理、安全管理數據庫
Hbase:系統表空間,用於HBase內部表。default:那些沒有定義表空間的表都被自動分配到這個表空間下緩存
Table(表):一個表由一個或者多個列族組成。數據屬性,好比超時時間(TTL),壓縮算法(COMPRESSION)等,都在列族的定義中定義。定義完列族後表是空的,只有添加了行,表纔有數據安全
Row(行):一個行包含了多個列,這些列經過列族來分類。行中的數據所屬列族只能從該表所定義的列族中選取,不能定義這個表中不存在的列族,不然你會獲得一個NoSuchColumnFamilyException。因爲HBase是一個列式數據庫,因此一個行中的數據能夠分佈在不一樣的服務器上。每一個row都擁有惟一的rowKey(行鍵)來標定這個行的惟一性。每一個列都有多個版本,多個版本的值存儲在單元格(cell)中服務器
Column Family(列簇):HBase會盡可能把同一個列族的列放到同一個服務器上,這樣能夠提升存取性能,而且能夠批量管理有關聯的一堆列。全部的數據屬性都是定義在列族上。若干個列能夠被歸類爲一個列簇。同一個表中不一樣的列簇能夠有徹底不一樣的屬性配置,可是同一個列簇內的全部列都會有相同的屬性,由於他們都在一個列簇裏面,而屬性都是定義在列簇上的數據結構
以下圖爲表設置了兩個列族,並且,定義了每個列族中要存放的列,如圖所示:{Name} -> Column Family-A, {City, Phone, Gender} -> Column Family-B,不一樣列族的數據會被存儲在不一樣的路徑中。即,設置多個列族時一行數據可能存在於兩個路徑中。整行讀取的時候,須要將兩個路徑中的數據合併在一塊兒才能夠獲取到完整的一行記錄。但若是僅僅讀取Name一列的話,只須要讀取Column Family-A便可
架構
Column Qualifier(列):多個列組成一個行。列族和列常常用Column Family: Column Qualifier來一塊兒表示。列是能夠隨意定義的,一個行中的列不限名字、不限數量,只限定列族負載均衡
一個列中能夠存儲多個版本的數據。而每一個版本就稱爲一個單元格(Cell),因此在HBase中的單元格跟傳統關係型數據庫的單元格概念不同。HBase中的數據細粒度比傳
統數據結構更細一級,同一個位置的數據還細分紅多個版本oop
Timestamp(時間戳/版本號):你既能夠把它稱爲是時間戳,也能夠稱爲是版本號,由於它是用來標定同一個列中多個單元格的版本號的。當你不指定版本號的時候,系統會自動採用當前的時間戳來做爲版本號性能
rowkey(行鍵):Hbase沒法根據某個column來排序,永遠是根據rowkey來排序的。若是插入Hbase的時候,以前已經存在即將插入的rowkey,它會把以前的row更新掉,以前已經存在的column,會被放在這個單元格的歷史記錄裏面,並不會丟掉,只是你須要帶上版本參數才能夠找到這個值
rowkey的排序規則:每個行都有一個相似主鍵的rowkey,而這個rowkey在HBase中是嚴格按照字典排序的,也就是說11比2小,row11會排在row1和row2中間
cell(單元格):一個列上能夠存儲多個版本的單元格,單元格是數據存儲的最小單元。Hbase裏面惟一肯定一條數據的是:rowkey:column family:column:version,若是不寫版本號,Hbase會取最後一個版本的數據返回給你,若是用戶不指定版本號,他就是系統默認的時間給你
ACID就是Atomicity(原子性)、Consistency(一致性)、Isolation(隔離性)、Durability(持久性)的首字母縮寫。ACID是事務正確執行的保證。HBase部分支持了ACID
1、Hbase宏觀架構
Master:mast服務器負責維護表結構信息,實際的數據都存在regionServer服務器上。負責啓動的時候分配Region到具體的RegionServer,執行各類管理操做,好比Region的分割和合並,建立表、修改列族配置
RegionServer:通常一臺服務器只會安裝一個RegionServer,也能夠安裝多個。RegionServer上有一個或者多個Region。咱們讀寫的數據就存儲在Region上。若是你的HBase是基於HDFS的,那麼Region全部數據存取操做都是調用了HDFS的客戶端接口來實現的。客戶端讀取數據是直接鏈接regionServer的,因此當master掛掉以後依然能夠查詢數據,可是不能建表了。regionServer十分依賴ZK,ZK管理了全部Hbase的regionServer的信息,包括具體的字段存在哪一個RegionServer上。客戶端每次鏈接Hbase,都是先與ZK通訊,查詢出來那個regionServer須要鏈接,而後在鏈接RegionServer
Region:它是一段數據的集合,一個region是多個行(row)的集合,表的一部分數據。HBase是一個會自動分片的數據庫。一個Region就至關於關係型數據庫中分區表的一個分區,region有如下特性
1.Region不能跨服務器,一個RegionServer上有一個或者或者Region
二、數據量小的時候,一個Region能夠存儲全部數據;當數據量大的時候,Hbase會拆分Region
三、Hbase在負載均衡的時候,也有可能會從一臺ResgionServer上把Region移動到另外一臺RegionServer上
四、Region是基於Hdfs的,它的全部數據存取操做都是調用了HDFS的客戶端接口來實現的
HDFS:Hadoop的一部分。HBase並不直接跟服務器的硬盤交互,而是跟HDFS交互,因此HDFS是真正承載數據的載體
ZooKeeper:把Master服務器關掉你的業務系統照樣跑,能讀能寫;可是把ZooKeeper關掉,你就不能讀取數據了,由於你讀取數據所須要的元數據表hbase:meata的位置存儲在ZooKeeper上
一、RegionServer架構
一個WAL:預寫日誌,WAL是Write-Ahead Log的縮寫。從名字就能夠看出它的用途,就是:預先寫入。當操做到達Region的時候,HBase先無論三七二十一把操做寫到WAL裏面去。HBase會先把數據放到基於內存實現的Memstore裏,等數據達到必定的數量時才刷寫(flush)到最終存儲的HFile內。而若是在這個過程當中服務器宕機或者斷電了,那麼數據就丟失了。WAL是一個保險機制,數據在寫到Memstore以前,先被寫到WAL了。這樣當故障恢復的時候能夠從WAL中恢復數據
多個Region:Region至關於一個數據分片。每個Region都有起始rowkey和結束rowkey,表明了它所存儲的row範圍
1.一、Region結構
多個Store:每個Region內都包含有多個Store實例。一個Store對應一個列族的數據,若是一個表有兩個列族,那麼在一個Region裏面就有兩個Store。在最右邊的單個Store的解剖圖上,咱們能夠看到Store內部有MemStore和HFile這兩個組成部分。
1.1.1 Store 結構
MemStore:每一個Store中有一個MemStore實例。數據寫入WAL以後就會被放入MemStore。MemStore是內存的存儲對象,只有當MemStore滿了的時候纔會將數據刷寫(flush)到HFile中。
HFile:在Store中有多個HFile。當MemStore滿了以後HBase就會在HDFS上生成一個新的HFile,而後把MemStore中的內容寫到這個HFile中。HFile直接跟HDFS打交道,它是數據的存儲實體
WAL是存儲在HDFS上的,Memstore是存儲在內存中的,HFile又是存儲在HDFS上的。數據是先寫入WAL,再被放入Memstore,最後被持久化到HFile中。
數據在進入HFile以前已經被存儲到HDFS一次了,爲何還須要被放入Memstore?
這是由於HDFS上的文件只能建立、追加、刪除,可是不能修改。那就是使用內存先把數據整理成順序存放,而後再一塊兒寫入硬盤。這就是Memstore存在的意義。Memstore存在的意義是維持數據按照rowkey順序排列,而不是作一個緩存
1.1.2 MemStore
數據被寫入WAL以後就會被加載到MemStore中去。MemStore的大小增長到超過必定閥值的時候就會被刷寫到HDFS上,以HFile的形式被持久化起來
(1)因爲HDFS上的文件不可修改,爲了讓數據順序存儲從而提升讀取效率,HBase使用了LSM樹結構來存儲數據。數據會先在Memstore中整理成LSM樹,最後再刷寫到HFile上。不過不要想固然地認爲讀取也是先讀取Memstore再讀取磁盤喲!讀取的時候是有專門的緩存叫BlockCache,這個BlockCache若是開啓了,就是先讀BlockCache,讀不到纔是讀HFile+Memstore,
(2)優化數據的存儲。好比一個數據添加後就立刻刪除了,這樣在刷寫的時候就能夠直接不把這個數據寫到HDFS上
每一次的刷寫都會產生一個全新的HFile文件,因爲HDFS的特性,因此這個文件不可修改
memstore內部存儲使用的是跳錶。Hbase屬於LSM tree結構的數據庫,LSM tree 結構的數據庫有一個特色,實時寫入的數據先寫入內存,內存達到閾值再往磁盤flush的時候,會生成相似storFile的有序文件,而跳錶就是自然有序的,因此在flush的時候效率很高,並且跳錶查找、插入刪除性能都很高,這個就是Hbase memstore內部存儲使用跳錶的緣由之一。Hbase使用的是JUC下面的concurrentSkipListMap()
1.1.3 HFile
HFile是數據存儲的實際載體,咱們建立的全部表、列等數據都存儲在HFile裏面
咱們能夠看到HFile是由一個一個的塊組成的。在HBase中一個塊的大小默認爲64KB,由列族上的BLOCKSIZE屬性定義。
Data:數據塊。每一個HFile有多個Data塊。咱們存儲在HBase表中的數據就在這裏。Data塊實際上是可選的,可是幾乎很難看到不包含Data塊的HFile。
Meta:元數據塊。Meta塊是可選的,Meta塊只有在文件關閉的時候纔會寫入。Meta塊存儲了該HFile文件的元數據信息,
FileInfo:文件信息,其實也是一種數據存儲塊。FileInfo是HFile的必要組成部分,是必選的。它只有在文件關閉的時候寫入,存儲的是這個文件的信息,好比最後一個Key(LastKey),平均的Key長度(Avg Key Len)等。
DataIndex:存儲Data塊索引信息的塊文件。索引的信息其實也就是Data塊的偏移值(offset)。DataIndex也是可選的,有Data塊纔有DataIndex。
MetaIndex:存儲Meta塊索引信息的塊文件。MetaIndex塊也是可選的,有Meta塊纔有MetaIndex。
Trailer:必選的,它存儲了FileInfo、DataIndex、MetaIndex塊的偏移值
布隆過濾器:全部數據在Hbase中都以一個個HFile爲單元來存儲,一個HFile大小概爲幾百兆字節到幾千兆字節,每個檢索請求到來的時候,掃描器都會從頭至尾地掃描整個HFile。爲了提升HFile的檢索速度,HBase使用了塊索引機制。原理就是在HFile中增長一個部分,單獨存儲該HFile中的全部行鍵,這樣掃描器能夠先經過檢索塊索引來查找行鍵,當找到行鍵後再去具體的位置獲取該行的其餘信息。
布隆過濾器能夠知道元素在集合中是否「不存在」或者「可能存在」,也就是說若是布隆過濾器認爲該元素不存在,那麼就是不存在。這樣能夠極大地加速檢索的速度,由於當布隆過濾器認爲要檢索的數據不在該塊索引中時,掃描器能夠跳過那些絕對不須要掃描的塊索引
1.2 WAL預寫日誌
預寫日誌(Write-ahead log,WAL)就是設計來解決宕機以後的操做恢復問題的。數據到達Region的時候是先寫入WAL,而後再被加載到Memstore的。就算Region的機器宕掉了,因爲WAL的數據是存儲在HDFS上的,因此數據並不會丟失
延遲寫入WAL:當你的改動,好比Put、Delete、Append來到Region的時候會先放在內存中,這些改動馬上就會被寫入WAL。
Region會等到條件知足的時候才把操做寫入WAL。這裏提到的條件主要指的是時間間隔hbase.regionserver.optionallogflushinterval,這個時間間隔的意思是HBase間隔多久會把操做從內存寫入WAL,默認值是1s
WAL滾動:WAL必定是一個環狀的滾動日誌結構,由於這種結構寫入效果最高,並且能夠保證空間不會持續變大。
WAL滾動的條件是:
一、WAL的檢查間隔由hbase.regionserver.logroll.period定義,默認值爲1小時。檢查的內容是把當前WAL中的操做跟實際持久化到HDFS(HFile中)上的操做比較,看哪些操做已經被持久化了,被持久化的操做就會被移動到.oldlogs文件夾內(這個文件夾也是在HDFS上的)。
二、當WAL文件所在的塊(Block)快要滿了。
三、當WAL所佔的空間大於或者等於某個閥值:你若是是基於HDFS的,默認WAL所佔塊空間大於或等於95%塊大小,這個WAL文件就會被歸檔到.oldlogs文件夾內
刪除.oldlogs條件:
Master會負責按期地去清理.oldlogs文件夾,條件是「當這個WAL不須要做爲用來恢復數據的備份」的時候。判斷的條件是「沒有任何引用指向這個WAL文件。
一、TTL進程:該進程會保證WAL文件一直存活直到達到hbase.master.logcleaner.ttl定義的超時時間(默認10分鐘)爲止
二、備份(replication)機制:若是你開啓了HBase的備份機制,那麼HBase要保證備份集羣已經徹底不須要這個WAL文件了,纔會刪除這個WAL文件。
2、增刪改查
一、當你新增一個單元格的時候,HBase在HDFS上新增一條數據。
二、當你修改一個單元格的時候,HBase在HDFS又新增一條數據,只是版本號比以前那個大(或者你本身定義)。
三、當你刪除一個單元格的時候,HBase仍是新增一條數據!只是這條數據沒有value,類型爲DELETE,這條數據叫墓碑標記(Tombstone)。
真正的刪除數據
Compaction會從一個region的一個store中選擇一些hfile文件進行合併。合併說來原理很簡單,先從這些待合併的數據文件中讀出KeyValues,再按照由小到大排列後寫入一個新的文件中。以後,這個新生成的文件就會取代以前待合併的全部文件對外提供服務。HBase根據合併規模將Compaction分爲了兩類:MinorCompaction和MajorCompaction
因爲數據庫在使用過程當中積累了不少增刪查改操做,數據的連續性和順序性必然會被破壞。爲了提高性能,HBase每間隔一段時間都會進行一次合併(Compaction),合併的對象爲HFile文件。合併分爲兩種minor compaction和major compaction。
在HBase進行major compaction的時候,它會把多個HFile合併成1個HFile,在這個過程當中,一旦檢測到有被打上墓碑標記的記錄,在合併的過程當中就忽略這條記錄。這樣在新產生的HFile中,就沒有這條記錄了,天然也就至關於被真正地刪除了
Minor Compaction是指選取一些小的、相鄰的StoreFile將他們合併成一個更大的StoreFile,在這個過程當中不會處理已經Deleted或Expired的Cell。一次Minor Compaction的結果是更少而且更大的StoreFile
3、總結概括
一、一個RegionServer包含多個Region,劃分規則是:一個表的一段鍵值在一個RegionServer上會產生一個Region。不過當你1行的數據量太大了(要很是大,不然默認都是不切分的),HBase也會把你的這個Region根據列族切分到不一樣的機器上去。
二、一個Region包含多個Store,劃分規則是:一個列族分爲一個Store,若是一個表只有一個列族,那麼這個表在這個機器上的每個Region裏面都只有一個Store。
三、一個Store裏面只有一個Memstore。
四、一個Store裏面有多個HFile(StoreFile是HFile的抽象對象,因此若是說到StoreFile就等於HFile)。每次Memstore的刷寫(flush)就產生一個新的HFile出來
寫入和讀出過程
一、寫入
WAL:數據被髮出以後第一時間被寫入WAL。因爲WAL是基於HDFS來實現的,因此也能夠說如今單元格就已經被持久化了,可是WAL只是一個暫存的日誌,它是不區分Store的。這些數據是不能被直接讀取和使用。
Memstore:數據隨後會當即被放入Memstore中進行整理。Memstore會負責按照LSM樹的結構來存放數據。這個過程就像咱們在打牌的時候,抓牌以後在手上對牌進行整理的過程。
HFile:最後,當Memstore太大了達到尺寸上的閥值,或者達到了刷寫時間間隔閥值的時候,HBaes會被這個Memstore的內容刷寫到HDFS系統上,稱爲一個存儲在硬盤上的HFile文件。至此,咱們能夠稱爲數據真正地被持久化到硬盤上,就算宕機,斷電,數據也不會丟失了
二、讀出
讀取順序是先從BlockCache中找數據,找不到了再去Memstore和HFile中查詢數據
爲了要過濾墓碑標記的數據,HBase的Scan操做在取到所須要的全部行鍵對應的信息以後還會繼續掃描下去,直到被掃描的數據大於給出的限定條件爲止,這樣它才能知道哪些數據應該被返回給用戶,而哪些應該被捨棄。因此你增長過濾條件也沒法減小Scan遍歷的行數,只有縮小STARTROW和ENDROW之間的行鍵範圍才能夠明顯地加快掃描的速度。在Scan掃描的時候store會建立StoreScanner實例。StoreScanner會把MemStore和HFile結合起來掃描。其中紅色塊部分都是屬於指定row的數據,Scan要把全部符合條件的StoreScanner都掃描過一遍以後纔會返回數據給用戶
一、客戶端經過 ZooKeeper 以及-ROOT-表和.META.表找到目標數據所在的 RegionServer(就是 數據所在的 Region 的主機地址)
二、聯繫 RegionServer 查詢目標數據
三、RegionServer 定位到目標數據所在的 Region,發出查詢請求
四、在BlockCache中查找,命中則返回
五、Region 在 Memstore 中查找,命中則返回
六、若是在 Memstore 中找不到,則在 HFile 中掃描 爲了能快速的判斷要查詢的數據在不在這個 HFile 中,應用了 BloomFilter
Region 定位
(1)客戶端先經過ZooKeeper的/hbase/meta-region-server節點查詢到哪臺RegionServer上有hbase:meta表。
(2)客戶端鏈接含有hbase:meta表的RegionServer。hbase:meta表存儲了全部Region的行鍵範圍信息,經過這個表就能夠查詢出你要存取的rowkey屬於哪一個Region的範圍裏面,以及這個Region又是屬於哪一個RegionServer。
(3)獲取這些信息後,客戶端就能夠直連其中一臺擁有你要存取的rowkey的RegionServer,並直接對其操做。
(4)客戶端會把meta信息緩存起來,下次操做就不須要進行以上加載hbase:meta的步驟了
habse:meta.:是一張元數據表,它存儲了全部Region的簡要信息。.META.表中的一行記錄就是一個Region,該行記錄了該Region的起始行、結束行和該Region的鏈接信息,這樣客戶端就能夠經過這個來判斷須要的數據在哪一個Region上。
4、rowkey設計
一、reversing
初步設計出的RowKey在數據分佈上不均勻,但RowKey尾部的數據卻呈現出了良好的隨機性,此時,能夠考慮將RowKey的信息翻轉,或者直接將尾部的bytes提早到RowKey的前部
缺點:場景利於Get但不利於Scan,由於數據在原RowKey上的天然順序已被打亂
二、salting
Salting的原理是在原RowKey的前面添加固定長度的隨機bytes,隨機bytes能保障數據在全部Regions間的負載均衡
缺點:既然是隨機bytes,基於原RowKey查詢時沒法獲知隨機bytes信息是什麼,也就須要去各個可能的Regions中去查看。可見, Salting對於讀取是糟糕的
三、hashing
基於RowKey的完整或部分數據進行Hash,然後將Hashing後的值完整替換原RowKey或部分替換RowKey的前綴部分
缺點:與Reversing相似, Hashing也不利於Scan,由於打亂了原RowKey的天然順序。
5、Hbase優化
一、blockCache
(1)LRUBlock Cache
近期最少使用算法的縮寫。讀出來的block會被放到BlockCache中待下次查詢使用。當緩存滿了的時候,會根據LRU的算法來淘汰block。LRUBlockCache被分爲三個區域:
列族的IN-MEMORY:被設置爲IN-MEMORY並非意味着這個列簇是存儲在內存中的,這個列簇依然是跟別的列簇同樣存儲在硬盤上,通常的Block被第一次讀出後是放到single-access的,只有當被訪問屢次後纔會放到multi-access,而帶有IN-MEMORY屬性的列族中的Block一開始就被放到in-memory區域。這個區域的緩存有最高的存活時間,在須要淘汰Block的時候,這個區域的Block是最後被考慮到的
LRUBlockCache內存使用比例默認是0.4。要注意在Hbase內存使用上MemStore+BlockCache內存比例不能超過0.8,不然就會報錯。
使用徹底基於JVM Heap的緩存帶來的後果就是:內存中的對象愈來愈多,每隔一段時間就會發生FULL GC
(2)Bucket Cache
BucketCache能夠本身劃份內存空間,本身管理內存空間,Block放進去的時候會考慮offset偏移量,因此內存碎片少,發生GC時間短
#使用的存儲介質,能夠爲heap/offheap/file,默認offheap hbase.bucketcache.ioengine #是否打開組合模式,默認爲true hbase.bucketcache.combinedcache.enabled #BucketCache所佔大小,0.0-1.0表明站堆內存比例,大於1的值表示MB爲單位的內存;默認爲0.0,即關閉BucketCache hbase.bucketcache.size #定義全部的Block種類,單位爲B,每一種類型必須是1024的整數倍 hbase.bucketcache.bucket.sizes #該值不是在hbase-site.xml中配置,而是一個啓動參數,默認按需獲取堆外內存,若是配置了,就至關於設置了堆外內存上限 -XX:MaxDirectMemorySize
(3)組合模式
說就是把不一樣類型的Block分別放到LRUCache和BucketCache中。
Index Block和Bloom Block會被放到LRUCache中。Data Block被直接放到BucketCache中,因此數據會去LRUCache查詢一下,而後再去BucketCache中查詢真正的數據。其實這種實現是一種更合理的二級緩存,數據從一級緩存到二級緩存最後到硬盤,數據是從小到大,存儲介質也是由快到慢。考慮到成本和性能的組合,比較合理的介質是:LRUCache使用內存->BuckectCache使用SSD->HFile使用機械硬盤
6、Hbase問題
一、Hbase多列簇
每一個regionServer有多個region,每一個region有多個store。每一個store包含一個memstore和多個storeFile
在Hbase表中,每一個列簇對應region中的一個store,region的大小達到閾值會分裂,所以若是表中有多個列簇的時候,可能會出現如下狀況
(1) 一個region中有多個store:若是每一個CF的數據分佈不均勻,好比CF1有100萬,CF2有1萬,則region分裂會致使CF2在每一個region中數據量太少,查詢CF2時候會橫跨多個region致使效率變低
(2) 若是每一個CF均勻分部:CF1爲50萬,CF250萬,CF350萬,則region分裂時候,致使每一個CF在region的數據量偏少,查詢某個CF時會致使很快多個Region的機率增大
(3)多個CF:有多個CF表明有多個store,也就是有多個memestore,也就是會致使內存的開銷增大,使得效率下降
(4)緩存刷新和壓縮:region中的緩存刷新和壓縮是基本操做,即一個CF出現緩存刷新或壓縮操做,其餘CF也會作同同樣的操做,當列簇太多時就會致使IO頻繁