摘要: 大家在使用HBase和Solr搭建系統中經常遇到的一個問題就是:“我通過SOLR得到了RowKeys后,該怎樣去HBase上取數據”。使用現有的Filter性能差勁,網上也沒有現成的自定義Filter解決方案,我在這里把這個問題的解決辦法分享給大家,拋磚引玉一下。先講一下,雖然使用自定義過濾器來達到取數據的目的,但它其實並不是一個好的解決辦法,因為它的性能是有問題的,具體分析還要看我的博客HBase 高性能獲取數據 - 多線程批量式解決辦法:http://www.cnblogs.com/wgp13x/p/4245182.html
Solr和HBase專輯
1、“關於Solr的使用總結的心得體會”(http://www.cnblogs.com/wgp13x/p/3742653.html)
2、“中文分詞器性能比較”(http://www.cnblogs.com/wgp13x/p/3748764.html)
3、“Solr與HBase架構設計”(http://www.cnblogs.com/wgp13x/p/a8bb8ccd469c96917652201007ad3c50.html)
4、 “大數據架構: 使用HBase和Solr將存儲與索引放在不同的機器上”(http://www.cnblogs.com/wgp13x/p/3927979.html)
5、“一個自定義 HBase Filter -通過RowKeys來高性能獲取數據”(http://www.cnblogs.com/wgp13x/p/4196466.html)
“大數據架構: 使用HBase和Solr將存儲與索引放在不同的機器上”此文一經推出,覽者眾多,也有很多同仁朋友問我問題,看來近年關注先進大數據架構的公司越來越多了,基本上有數據接入的公司都會有這一問題。具我所知,“我該選Oracle還是MySQL作數據存儲呢?”,“MySQL數據庫索引創建后對雙百分號%like不起作用啊!”,“沒事,數據量再大我加存儲櫃不就行了?哈哈哈!”,這些都是還在用關系型數據庫存儲大數據的公司,它們往往對查詢這塊束手無策;它們的對策往往是:“我退而求其次,往年的數據我搜不了,我只搜今年的數據不就OK啦?反正業務單位我也能搞定。”--這是有壟斷有背景的公司,也是不思進取的公司,“我分表,用戶查近三個月的數據多,查前面的數據少,我每個月分一張表,解決了。”--這是吃了這頓沒下頓的公司,抓緊把當前的項目搞完拿錢走人的公司,“上內存數據庫哎,那個快。”--這是不管三七二十一、以屁股決定腦袋的公司。不管怎么說,能關注當前大數據架構技術的公司都是不落伍滴。
現在我把大家在使用HBase和Solr搭建系統中經常遇到的一個問題的解決辦法在這里分享給大家,這個問題就是:“我通過SOLR得到了RowKeys后,該怎樣去HBase上取數據”。
相信有人開始發問了,HBase不是提供了功能很強勁的Filter了嗎?Comparision Filters,Dedicated Filters,Decorating Filters,可以或可以且,可以相等,還可以組合,看起來很豐滿,用起來很骨感。經過一番實踐過后,性能低得嚇人,使用200條RowKey在300百萬的數據集中取數據,要等上好幾鍾,究其原因,多Filter的或組合過濾就是在HBase的數據里滾了一遍又一遍,不慢才怪。
那么應該怎么做呢?自定義HBase Filter,這樣只要滾一遍就好啦。RowKeyFilter就是自定義的Filter,它繼承自FilterBase類,類中主要定義了三個屬性,byte[] value用來傳入RowKeys,Map<Object, Object>用來存放傳入的RowKeys,撞到了就返回,boolean filterRow用來告訴HBase是否濾掉。下面是具體的RowKeyFilter代碼。
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.apache.hadoop.hbase.KeyValue;
import org.apache.hadoop.hbase.filter.FilterBase;
import org.apache.hadoop.hbase.util.Bytes;
/**
* @description 自定義過濾器,用來讀取大量離散行
* @author 王安琪
* @time 2014年11月8日上午10:47:17
* @className RowKeyFilter
*/
public class RowKeyFilter extends FilterBase
{
private byte[] value = null;
private boolean filterRow = true;
/**
* map中存放需要讀的行RowKey
*/
public Map<Object, Object> map = new HashMap<Object, Object>();
public RowKeyFilter()
{
super();
}
public RowKeyFilter(byte[] value)
{
this.value = value;
}
@Override
public ReturnCode filterKeyValue(KeyValue ignored)
{
if (this.filterRow == false)
return ReturnCode.INCLUDE;
else
return ReturnCode.NEXT_ROW;
}
/**
* 行過濾,查詢該行RowKey是否在Map中
*/
@Override
public boolean filterRowKey(byte[] buffer, int offset, int length)
{
byte[] rowKey = Arrays.copyOfRange(buffer, offset, offset + length);
String str = new String(rowKey);
if (map.containsKey(str))
{ // 在0(1)時間內返回,效率較高
this.filterRow = false; // false表示包括這一行
}
return this.filterRow;
}
@Override
public void reset()
{
this.filterRow = true;
}
@Override
public boolean filterRow()
{
return filterRow;
}
/**
* 將Map中的數據以Byte[]形式傳給服務器
*/
@Override
public void write(DataOutput dataOutput) throws IOException
{
Bytes.writeByteArray(dataOutput, this.value);
}
/**
* 服務器讀取Byte[]數據,再將數據存儲到Map中 不同的RowKey以","分割
*/
@Override
public void readFields(DataInput dataInput) throws IOException
{
this.value = Bytes.readByteArray(dataInput);
String string = new String(this.value);
String[] strs = string.split(",");
for (String str : strs)
{
map.put(str, str);
}
}
} |
自定義Filter如何加載到HBase中,網上有很多的介紹,這里就不作說明了,下面列舉出了如何使用RowKeyFilter的代碼段來達到篩選的目的。
/**
* 根據rowKeys獲取數據
*
* @param rowKeys:每個rowkey之間使用逗號分隔符
* @param filterColumn:表示過濾的列,如果為空表示所有列的數據都返回
* @param isContiansRowkeys:設置為true,表示返回結果集中包含rowkeys;否則返回結果集中不包含rowkeys
* @return
*/
/* @Override */
public Datas getDatasFromHbase(String rowKeys, List<String> filterColumn,
boolean isContiansRowkeys)
{
Datas datas = new Datas();
HTableInterface hTableInterface = getTable(tableName);
Scan scan = new Scan();
if (filterColumn != null)
{
for (String column : filterColumn)
{
scan.addColumn(columnFamilyName.getBytes(), column.getBytes());
}
}
if (rowKeys != null && rowKeys.length() > 0)
{
RowKeyFilter rowKeyFilter = new RowKeyFilter(rowKeys.getBytes());
scan.setFilter(rowKeyFilter);
}
ResultScanner resultScanner = null;
List<Data> listData = new ArrayList<Data>();
try
{
resultScanner = hTableInterface.getScanner(scan);
for (Result result : resultScanner)
{
Data data = new Data();
if (isContiansRowkeys)
{
data.setRowkey(new String(result.getRow()));
}
Map<String, String> map = new HashMap<String, String>();
List<String> content = new ArrayList<String>();
String[] temp = null;
if (filterColumn != null)
{
temp = new String[filterColumn.size()];
}
for (KeyValue keyValue : result.raw())
{
if (filterColumn == null)
{
content.add(new String(keyValue.getValue()));
}
else if (filterColumn != null)
{
String qualifier = new String(keyValue.getQualifier());
String value = new String(keyValue.getValue());
if (filterColumn.contains(qualifier))
{
int index = filterColumn.indexOf(qualifier);
temp[index] = value;
}
}
}
if (temp != null)
{
for (int i = 0; i < temp.length; i++)
{
content.add(temp[i]);
}
}
data.setContent(content);
listData.add(data);
}
datas.setDatas(listData);
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
resultScanner.close();
try
{
hTableInterface.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
return datas;
} |
經過驗證,在MySQL單表數量為1000萬時,過濾主鍵300個ID共花費20s左右;在HBase單表數據量達到1000萬條時,過濾300條RowKey的數據共花了10多秒,顯然此Filter的性能比自帶Filter的性能提高了不少,還要優於MySQL主鍵獲取,但我覺得尚且不夠,性能還遠遠達不到目標要求。這種方式肯定不能達到高速度取數據的目的,高性能方法還要看我最新的博客:HBase 高性能獲取數據 - 多線程批量式解決辦法:http://www.cnblogs.com/wgp13x/p/4245182.html!

另,有掛靠《系統集成項目管理師》的公司嗎?我剛考過,有需要的Q我詳聊。寫在2014年末。