一個自定義 HBase Filter -“通過RowKeys來高性能獲取數據”


摘要:  大家在使用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年末。
 




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM