HBase的布隆过滤器详解
HBase的布隆過濾器詳解
- 1.布隆過濾器的簡單介紹
- 2.布隆過濾器的原理分析
- 2.1 哈希表存在的問題
- 2.2 布隆過濾器的原理
- 2.2.1 原理詳解
- 2.2.2 布隆過濾器失誤率的調節
- 2.2.3 布隆過濾器的完整體
- 3.HBase是如何使用布隆過濾器的
- 3.1 HBase的基本回顧
- 3.2 HBase的塊索引機制及存在的問題
- 3.3 HBase布隆過濾器的作用
- 4.大戰后的悠閑時光
- 4.1 布隆過濾器的存儲在哪?
- 4.2 如何開啟布隆過濾器
- 4.3 采用布隆過濾器后, HBase如何get數據
- 4.4 采用ROW還是ROWCOl布隆過濾器
- 5.參考博客
1.布隆過濾器的簡單介紹
?布隆過濾器(Bloom Filter): 由一個很長的二進制向量和一系列隨機的哈希函數組成. 它的作用不是過濾數據, 而是判斷當前這條數據是否已經存在 在了當前的數據集合中.
2.布隆過濾器的原理分析
2.1 哈希表存在的問題
?hash是內存中使用的經典數據結構.
?當我們需要判斷一個元素是否在一個集合當中時, 我們可以先對集合生成一個hash表, 將當前元素轉為對應的hash值(用的是同一個hash函數), 然后判斷當前hash值是否在hash表中, 即可知元素是否在集合中.
?然而在大數據場景下(PB), 很多時候, hash就顯得力不從心. 因為在海量數據下hash要占用大量的內存空間, 且遠遠超過了我們能提供的內存大小
?例如在黑名單過濾當中, 我們有100億的網站黑名單url需要過濾, 家事兒一個url是64bytes. 如果我們用hash 表, 那么至少需要6400億字節 即640GB的內存空間(實際所需空間還遠大于此), 空間消耗巨大, 必須要多個服務器來同時分攤內存.
2.2 布隆過濾器的原理
?在上述背景下, 我們就需要使用更加精簡的結構來替代hash表, 布隆過濾器就是這樣一個高度節省空間的結構, 并且其時間也元超一般算法.
2.2.1 原理詳解
?布隆過濾器實際就是一種集合. 假設我們有一個數組, 它的長度為L, 從0 ~ (L-1)位置上, 存儲的不是一個字符串或整數, 而是一個bit, 這意味著它的取值只能為0或1.
?例如我們實現如下的一個數組:
??int[] array = new int[1000];
?1 int = 4byte = 32bit, 所以我們申請的array數組中包含有32000個bit位. 此時我們想將第20003個bit位描黑, 將其改為1, 這樣我們需要怎么做呢?
?首先我們需要定位, 這第20003個bit位于array的索引值, 接著我們需要定位該bit位于該索引值的第幾個bit位.
??int index = 20003
??int intIndex = index / 32 向下取整
??int bitIndex = index % 32 取余
?描黑:
??array[intIndex] = (array[intIndex] | (1 << (bitIndex - 1)))
?上述代碼塊的解釋:
?intIndex = 625, bitIndex = 3, 所以第20003個bit位于array的第625索引位置(加1, 減1)的第3個bit位.
?value << num: 表示的是將value左移num位. 所以上述位運算符的右側就是將數值1 左移2位, 得到的結果為:
??0000000 00000000 00000000 00000100
?java中int類型的初始值為0, 所以array[intIndex] 與上述結果做或的位運算的結果就是將array[intIndex]的第3bit位賦值為1(描黑).
?有了這些基礎后, 我們如何設計黑名單問題呢?
?我們設置一個長度為m個bit的數組,將100億個黑名單網站url, 通過同一個hash函數計算出hashcode, 然后hashcode % m 將對應數組的bit位置描黑. 此時新來一個網站url, 計算其hashcode, 然后hashcore % m 后看對應的bit位上是否已經描黑, 描黑說明新的url是黑名單網站, 沒有描黑說明新的url是正常網站
? 但是布隆過濾器存在一定的失誤率,正常url的 hashcode % m
與黑名單中某個url的hashcode % m 的結果相同時就會出現失誤. 但好處就是: 布隆過濾器絕不會將黑名單中網頁查錯, 但是有可能將正常的網頁URL判定為黑名單當中的, 它的失誤可以說是寧可錯殺,不可放過. 不過布隆過濾器的失誤率是可調節的
2.2.2 布隆過濾器失誤率的調節
?很明顯通過增長array數組的長度可以減少失誤率, 但是也會帶來布隆過濾器占用的內存增加的問題, 所以另一種方式是使用多個hash函數, 這樣一個url(一條數據)就會有多個hashcode, 會在array中對應多個描黑bit, 一個url只有多個被描黑的bit位與某個黑名單url的描黑的bit位匹配時才說明當前url是黑名單網站.
2.2.3 布隆過濾器的完整體
?所以,正如本文開頭所說的: 布隆過濾器有一個二進制向量和一系列哈希函數組成. 二進制向量的長度和哈希函數的個數就影響著布隆過濾器的失誤率.
?首先, 我們要使用多少個哈希函數呢?
?這一點只與我們黑名單中URL的數量及二進制向量長度有關, 而與單個URL的大小無關(哈希函數的輸入域是無窮的). 它只要求滿足我們的哈希函數能夠接受URL這一參數類型即可.
?而我們的數組的大小長度與什么有關呢?
?它同樣與我們黑名單中URL的數量有關, 除此之外它還與我們能夠接受的失誤率有關.
?下面我們給出有關公式:
? ?m = -n * lnp / (ln2) ^ 2 # n為樣本數量, p為預計的失誤率
?當我們的樣本量為100億, 而我們預計的失誤率為萬分之一, 根據這個公式我們便可以得到m為: 131571428572bit ~ 23GB(向上取整).對比hash表, 原來我們需要640GB, 而現在只需要23GB, 大大節省了內存空間的消耗. 而我們所需要的哈希函數的個數k的數學公式為:
? ?k = ln2 * m / k # m為數組長度, n為樣本數量, k向上取整
?這里經過計算n為13
?因為我們的m和n都是經過向上取整, 所以我們的實際失誤率會變得更低, 失誤率的計算公式為:
? ?p = (1 - e(-k*n/m))^k ?#n為樣本數量, m為數組長度, k為哈希函數個數
?經過我們向上取整后, 計算出來的實際失誤率為十萬分之六.
?所以布隆過濾器相當于將數據集中的每條數據做了一下轉換存儲, 存儲在了一個Array[Bit]中, 但其目的不是為了存儲, 而是為了判斷新的一條數據是否也在這個Array[Bit]中
3.HBase是如何使用布隆過濾器的
?布隆過濾器是hbase中的高級功能,是列族下的一個屬性, 它能夠減少特定訪問模式(get/scan)下的查詢時間. 不過由于這種模式增加了內存和存儲的負擔,所以被默認認為關閉狀態.
?hbase支持如下類型的布隆過濾器:
??1、NONE ?不使用布隆過濾器
??2、ROW ?行鍵使用布隆過濾器
??3、ROWCOL ?行列鍵一起使用布隆過濾器
?其中ROWCOL是粒度更細的模式.
?ROW: 將一個列族中的所有rowkey取hashcore, 然后去對應的二進制向量中描黑.
?ROWCOL:將一個列族下的所有rowkey+ 列值取hashcode, 然后去對應的二進制向量中描黑
3.1 HBase的基本回顧
?我們知道HBase的實際存儲結構是HFile, 它是位于HDFS系統中的, 也就是在磁盤中, 而加載到內存中的數據存儲在memstore, 當memstore中的數據達到一定閾值shi,他會將數據存入HFile中.
?一個HFile是由多個數據塊(64KB)和一個索引塊組成的, HBase是通過塊索引來訪問這些數據塊的. 而索引是由每個數據塊的第一行數據的rowkey組成的.當HBase打開一個HFile時, 塊索引信息會優先加載到內存當中. 然后HBase會通過這些塊索引來查詢數據
?但是塊索引是相當粗粒度的, 我們可以簡單計算一下. 假設一行占用100bytes空間, 所以一個數據塊64KB, 所包含的行大概: (64 * 1024) / 100 ~ 700行.而我們只能從索引給出的一個數據塊的起始行開始查詢
?如果用戶隨機查找一個行鍵, 則這個行鍵很可能位于兩個開始鍵(即索引)之間的位置. 對應HBase來說, 它判斷你這個行鍵是否真實存在的唯一方法就是加載這個數據塊, 并且掃描它是否包含這個鍵.
?先思考兩個問題,:
??數據塊中的rowkey是否是連續的?
??數據塊之間的起始rowkey是否是連續的?
???都不是連續的, 因為HBase是支持隨機讀寫的, 且HBase支持多版本數據, 且對于刪除,修改操作的數據并不是直接刪除, 修改HBase中的數據, 而是新增這條數據, 并給這條數據打上刪除, 修改的標識, 且在minor合并后這些打上標識的數據還是存儲在HFile中, 只有major合并時, HBase才會根據標識做真正的刪除, 修改數據.
?所以由上述兩個問題得出:
??1. 一條數據(一個rowkey)可能會在一個HFile中找到至多一個數據塊(根據起始rowkey)(至多一個: 因為在memstore, minor, major中都有排序).
??2.但找到的對應的數據塊中也不一定含有rowkey對應的數據
?同時, 還存在很多情況使得這種情況更加復雜
?對于一個應用來說, 用戶通常會以一定的速率進行更新數據, 這就將導致內存中的數據被刷新到磁盤中, 并且之后系統會將他們合并成更大的存儲文件(minor). 在HBase的合并存儲文件的時候, 它僅僅會合并最近幾個存儲文件, 直到合并的存儲文件達到配置的最大大小. 最終系統會有很多的存儲文件(一個列族有很多的HFile), 所有的存儲文件都是候選文件,其可能包含用戶請求行鍵的單元格.如下圖所示:
?我們可以看到, 這些不同的文件(HFile)都來自同一列族, 所以他們的行鍵分布類似.
3.2 HBase的塊索引機制及存在的問題
?在介紹為什么HBase要引入布隆過濾器之前, 我們先來了解一下HBase存儲文件的塊索引機制
?由上圖, 我們假設當前列族對應的store下有6個HFile文件, 針對某個特定rowkey的塊索引機制為:
??1、首先根據rowkey到每個HFile中索引塊中查找包含當前rowkey的數據塊, 并返回. 假設每個HFile中剛好都有1個數據塊的起始rowkey滿足條件, 這樣就返回了6個數據塊.
??2、regionserver需要加載每一個數據塊來檢查該塊中是否真的包含有該rowkey對應的數據.
?所以HBase本身的塊索引機制的問題就是,返回的數據塊過多, 每個都需要加載, 確認其中是否有對應的數據.
3.3 HBase布隆過濾器的作用
?針對塊索引機制存在的問題, 我們對store中的每個HFile設置布隆過濾器(為每個HFile文件建立一個二進制向量), 此時針對特定rowkey的查詢就變為:
?1、首先rowkey對每個HFile的布隆過濾器使用, 如果當前rowkey對應的hashcode取余操作, 剛好對應上HFile文件中某條數據的二進制向量描黑, 說明當前HFile中一定還有rowkey對應的數據(不考慮布隆過濾器本身的誤差)
?2、由上圖知: 返回的數據塊由原來的6個, 減少為2個, 大大降低了regionsever加載數據塊的時間, 從而增加了HBase集群的吞吐量
4.大戰后的悠閑時光
4.1 布隆過濾器的存儲在哪?
?對于HBase而言, 當我們選擇采用布隆過濾器后, HBase會在生成storeFile(HFile)時包含一份布隆過濾器結構的數據, 稱其為MetaBlock; MetaBlock與DataBlock(真實的KeyValue數據)一起由LRUBlockCache維護. 所以, 開啟BloomFilter會有一定的存儲及內存cache開銷. 但是在大多數情況下, 這些負擔相對于布隆過濾器帶來的好處是可以接受的.
4.2 如何開啟布隆過濾器
?create ‘table’, {BLOOMFILTER => ‘ROW’}
?HColumnDescriptor.setBloomFilterType(NONE | ROW | ROWCOL)
4.3 采用布隆過濾器后, HBase如何get數據
?在讀取數據時, HBase會首先在布隆過濾器中查詢, 根據布隆過濾器的結果, 再在memstore中查詢, 最后再去對應的HFile中查詢.
4.4 采用ROW還是ROWCOl布隆過濾器
?這取決于用戶的使用模式, 如果用戶只做行掃描, 使用更加細粒度的行加列布隆過濾器不會有任何的幫助, 這種場景就應該使用行級布隆過濾器. 當用戶不能批量更新特定的一行, 并且最后的使用存儲文件都含有該行的一部分時, 行加列級的布隆過濾器更加有用
?例如: ROW 使用場景假設有2個Hfile文件hf1和hf2, hf1包含kv1(r1 cf:q1 v)、kv2(r2 cf:q1 v) hf2包含kv3(r3 cf:q1 v)、kv4(r4 cf:q1 v) 如果設置了CF屬性中的bloomfilter(布隆過濾器)為ROW,那么get(r1)時就會過濾hf2,get(r3)就會過濾hf1 。
ROWCOL使用場景假設有2個Hfile文件hf1和hf2, hf1包含kv1(r1 cf:q1 v)、kv2(r2 cf:q1 v) hf2包含kv3(r1 cf:q2 v)、kv4(r2 cf:q2 v) 如果設置了CF屬性中的bloomfilter為ROW,無論get(r1,q1)還是get(r1,q2),都會讀取hf1+hf2;而如果設置了CF屬性中的bloomfilter為ROWCOL,那么get(r1,q1)就會過濾hf2,get(r1,q2)就會過濾hf1。
5.參考博客
https://blog.csdn.net/qq_38180223/article/details/80922114
?好吧, 你要說我是在抄襲人家的, 我也認.
總結
以上是生活随笔為你收集整理的HBase的布隆过滤器详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 谈谈spark.sql.shuffle.
- 下一篇: 将shell命令结果直接输出到HDFS上