轉載自:http://blog.csdn.net/opensure/article/details/46453681
1、主要功能
提高隨機讀的性能
2、存儲開銷
bloom filter的數據存在StoreFile的meta中,一旦寫入無法更新,因為StoreFile是不可變的。Bloomfilter是一個列族(cf)級別的配置屬性,如果你在表中設置了Bloomfilter,那么HBase會在生成StoreFile時包含一份bloomfilter結構的數據,稱其為MetaBlock;MetaBlock與DataBlock(真實的KeyValue數據)一起由LRUBlockCache維護。所以,開啟bloomfilter會有一定的存儲及內存cache開銷。
3、控制粒度
a)ROW
根據KeyValue中的row來過濾storefile
舉例:假設有2個storefile文件sf1和sf2,
sf1包含kv1(r1 cf:q1 v)、kv2(r2 cf:q1 v)
sf2包含kv3(r3 cf:q1 v)、kv4(r4 cf:q1 v)
如果設置了CF屬性中的bloomfilter為ROW,那么get(r1)時就會過濾sf2,get(r3)就會過濾sf1
b)ROWCOL
根據KeyValue中的row+qualifier來過濾storefile
舉例:假設有2個storefile文件sf1和sf2,
sf1包含kv1(r1 cf:q1 v)、kv2(r2 cf:q1 v)
sf2包含kv3(r1 cf:q2 v)、kv4(r2 cf:q2 v)
如果設置了CF屬性中的bloomfilter為ROW,無論get(r1,q1)還是get(r1,q2),都會讀取sf1+sf2;而如果設置了CF屬性中的bloomfilter為ROWCOL,那么get(r1,q1)就會過濾sf2,get(r1,q2)就會過濾sf1
4、常用場景
1、根據key隨機讀時,在StoreFile級別進行過濾
2、讀數據時,會查詢到大量不存在的key,也可用於高效判斷key是否存在
5、舉例說明
假設x、y、z三個key存在於table中,W不存在
使用Bloom Filter可以幫助我們減少為了判斷key是否存在而去做Scan操作的次數
step1)分別對x、y、z運算hash函數取得bit mask,寫到Bloom Filter結構中
step2)對W運算hash函數,從Bloom Filter查找bit mask
如果不存在:三個Bit位至少有一個為0,W肯定不存在該(Bloom Filter不會漏判)
如果存在 :三個Bit位全部全部等於1,路由到負責W的Region執行scan,確認是否真的存在(Bloom Filter有極小的概率誤判)
6、源碼解析
1.get操作會enable bloomfilter幫助剔除掉不會用到的Storefile
在scan初始化時(get會包裝為scan)對於每個storefile會做shouldSeek的檢查,如果返回false,則表明該storefile里沒有要找的內容,直接跳過
if (memOnly == false && ((StoreFileScanner) kvs).shouldSeek(scan, columns)) { scanners.add(kvs); }
shouldSeek方法:如果是scan直接返回true表明不能跳過,然后根據bloomfilter類型檢查。
if (!scan.isGetScan()) { return true; } byte[] row = scan.getStartRow(); switch (this.bloomFilterType) { case ROW: return passesBloomFilter(row, 0, row.length, null, 0, 0); case ROWCOL: if (columns != null && columns.size() == 1) { byte[] column = columns.first(); return passesBloomFilter(row, 0, row.length, column, 0, column.length); } // For multi-column queries the Bloom filter is checked from the // seekExact operation. return true; default: return true; }
2.指明qualified的scan在配了rowcol的情況下會剔除不會用掉的StoreFile。
對指明了qualify的scan或者get進行檢查:seekExactly
// Seek all scanners to the start of the Row (or if the exact matching row // key does not exist, then to the start of the next matching Row). if (matcher.isExactColumnQuery()) { for (KeyValueScanner scanner : scanners) scanner.seekExactly(matcher.getStartKey(), false); } else { for (KeyValueScanner scanner : scanners) scanner.seek(matcher.getStartKey()); }
如果bloomfilter沒命中,則創建一個很大的假的keyvalue,表明該storefile不需要實際的scan
public boolean seekExactly(KeyValue kv, boolean forward) throws IOException { if (reader.getBloomFilterType() != StoreFile.BloomType.ROWCOL || kv.getRowLength() == 0 || kv.getQualifierLength() == 0) { return forward ? reseek(kv) : seek(kv); } boolean isInBloom = reader.passesBloomFilter(kv.getBuffer(), kv.getRowOffset(), kv.getRowLength(), kv.getBuffer(), kv.getQualifierOffset(), kv.getQualifierLength()); if (isInBloom) { // This row/column might be in this store file. Do a normal seek. return forward ? reseek(kv) : seek(kv); } // Create a fake key/value, so that this scanner only bubbles up to the top // of the KeyValueHeap in StoreScanner after we scanned this row/column in // all other store files. The query matcher will then just skip this fake // key/value and the store scanner will progress to the next column. cur = kv.createLastOnRowCol(); return true; }
這邊為什么是rowcol才能剔除storefile納,很簡單,scan是一個范圍,如果是row的bloomfilter不命中只能說明該rowkey不在此storefile中,但next rowkey可能在。而rowcol的bloomfilter就不一樣了,如果rowcol的bloomfilter沒有命中表明該qualifiy不在這個storefile中,因此這次scan就不需要scan此storefile了!
7、總結
1.任何類型的get(基於rowkey或row+col)Bloom Filter的優化都能生效,關鍵是get的類型要匹配Bloom Filter的類型
2.基於row的scan是沒辦法走Bloom Filter的。因為Bloom Filter是需要事先知道過濾項的。對於順序scan是沒有事先辦法知道rowkey的。而get是指明了rowkey所以可以用Bloom Filter,scan指明column同理。
3.row+col+qualify的scan可以去掉不存在此qualify的storefile,也算是不錯的優化了,而且指明qualify也能減少流量,因此scan盡量指明qualify