1. 程式人生 > >億級資料多條件組合查詢——秒級響應解決方案

億級資料多條件組合查詢——秒級響應解決方案

1 概述

組合查詢為多條件組合查詢,在很多場景下都有使用。購物網站中通過勾選類別、價格、銷售量範圍等屬性來對所有的商品進行篩選,篩選出滿足客戶需要的商品,這是一種典型的組合查詢。在小資料量的情況下,後臺通過簡單的sql語句便能夠快速過濾出需要的資料,但隨著資料量的增加,繼續使用sql語句,查詢效率會直線下降。當資料量達到一定的量級,伺服器將會不堪重負甚至面臨掛掉的危險,並且大資料量的儲存也成為了一個問題。本文將討論在億級資料的情況下,多條件組合查詢秒級響應的解決方案。

2 方案思考

2.1 資料儲存

假定每條資料有10個欄位,每個欄位的大小為4Byte,共有1億條資料。通過傳統的關係型資料庫mysql,使用JDBC批處理和事務混合的方式對資料進行插入,插入一億資料大約需要半小時,欄位可能會出現為空的情況,導致冗餘。針對海量資料的儲存,現如今使用較多的是HBase。使用HBase的好處有三:其一,它是非關係型資料庫,欄位為空的值只在邏輯上存在,在空間上不存在,因此解決了冗餘的問題;其二,它是面向列的資料庫,能夠通過簡單的API呼叫對欄位進行橫向擴充套件;其三,它是分部署資料庫,表的RowKey 按照字典排序,Region按照RowKey設定split point進行shard,通過這種方式實現全域性、分散式索引,通過RowKey索引資料能夠在毫秒級返回。Hbase插入資料可以呼叫批量插入或者通過MR程式插入,實測在批量提交資料條數設定為1000,開10個執行緒的情況下,插入一億資料大約需要10分鐘。若需要加速插入速度,可以通過增加批量提交數、調整執行緒數或者使用MR程式進行Hbase的寫入。Hbase本身是分散式資料庫,資料儲存可以儲存在多個節點上,使用Zookeeper統一管理,提供資料備份和故障恢復的功能。因此使用Hbase作為資料倉庫,對結構化資料進行儲存。

2.2 資料查詢

Hbase中的資料查詢只有兩種方式:一是使用get 'tablename', 'rowkey‘’直接通過rowkey進行查詢,億級資料的查詢結果可以在毫秒內返回;二是設定過濾器對全表進行Scan掃描,該查詢方式在海量資料的情況下耗時十分長,當然也和伺服器的效能有關。我們的需求是秒級響應,如果使用全表掃描方式,資料量達到萬級或者十萬級就無法實現實時響應了,要進行這樣的查詢,往往是要通過類似Hive、Pig等系統進行全表的MapReduce計算,這種方式既浪費了機器的計算資源,又因高延遲使得應用黯然失色。因此我們考慮使用rowKey對資料進行查詢,如果我們使用rowKey對全表進行多條件組合查詢,這將對rowKey的設定要求十分高,面向業務而言這對程式設計師十分不友好,因此我們需要通過建立二級索引的方式,按索引的種類掃描各自獨立的單索引表,最後將掃描結果merge,得到目標rowKey。HBase有原生的建立二級索引的方式,即使用HBase的coprocessor協處理器,可以根據業務進行靈活的設定,但較為複雜,本文討論使用一種業務模式較為固定,但更加簡單直接的方式建立索引——Solr。Solr是一個獨立的企業級搜尋應用伺服器,是Apache Lucene專案的開源企業搜尋平臺。其主要功能包括全文檢索、命中標示、分面搜尋、動態聚類、資料庫整合,以及富文字(如Word、PDF)的處理。Solr是高度可擴充套件的,並提供了分散式搜尋和索引複製。我們可以直接使用Solr這一元件,通過修改配置檔案以實現相關的業務需求。通過批量建立索引的方式對HBase中的一億條資料的10個欄位構建索引,耗時為3383s

,約為1小時。具體程式碼如下:

public class ThreadsCreateIndexWork {
    private static Logger logger = LoggerFactory.getLogger(ThreadsCreateIndexWork.class);
    public static void main(String[] args) throws IOException, SolrServerException {
        if(args.length < 3) {
            logger.info("[tableName  |  queueSize  |  threadCount]"
); logger.info("e.g.| test1 20000 20"); } String tableName = args[0]; String queueSize = args[1]; String threadCount = args[2]; long start = System.currentTimeMillis(); final Configuration conf; Properties prop = PropertiesReaderUtils.getProperties("conf/path.properties"); String server = prop.getProperty("solr.server"); SolrServer solrServer = new ConcurrentUpdateSolrServer(server, Integer.parseInt(queueSize), Integer.parseInt(threadCount)); conf = HBaseConfiguration.create(); HTable table = new HTable(conf, tableName); // 這裡指定HBase表名稱 Scan scan = new Scan(); scan.addFamily(Bytes.toBytes("people")); // 這裡指定HBase表的列族 scan.setCaching(500); scan.setCacheBlocks(false); ResultScanner ss = table.getScanner(scan); try { for (Result r : ss) { SolrInputDocument solrDoc = new SolrInputDocument(); solrDoc.addField("rowkey", new String(r.getRow())); for (KeyValue kv : r.raw()) { String fieldName = new String(kv.getQualifier()); String fieldValue = new String(kv.getValue()); if (fieldName.equalsIgnoreCase("upperClothing") || fieldName.equalsIgnoreCase("lowerClothing") || fieldName.equalsIgnoreCase("coatStyle") || fieldName.equalsIgnoreCase("trousersStyle") || fieldName.equalsIgnoreCase("sex") || fieldName.equalsIgnoreCase("age") || fieldName.equalsIgnoreCase("angle") || fieldName.equalsIgnoreCase("bag") || fieldName.equalsIgnoreCase("umbrella") || fieldName.equalsIgnoreCase("featureType")){ solrDoc.addField(fieldName, fieldValue); } } solrServer.add(solrDoc); } ss.close(); table.close(); } catch (IOException e) { } finally { ss.close(); table.close(); } long time = System.currentTimeMillis() - start; logger.info("---------- create index with thread use time " + String.valueOf(time)); } }

ConcurrentUpdateSolrServer類可以使用多執行緒向SolrCloud的多個節點發送http請求,queueSize為佇列大小,即往Solr中一次性批量add的資料數目,threadCount為開啟的執行緒數,可以根據伺服器效能的不同進行自定義,以提高構建索引的速度。筆者對1000w條資料進行引數調整測試,得到如下結果:

queueSize threadNum Time (s)
10000 20 274
20000 20 250
20000 30 255
20000 40 254
30000 20 255

因此測試中選用的引數為queueSize:20000threadNum:20,索引構建速度為4w/s,還嘗試通過修改scan.setCaching(500);的大小來提高構建速度,但是發現該快取大小對構建速度的影響可以忽略不計,應該是索引構建速度低於HBase的Scan速度,因此暫時沒有必要對HBase的Scan操作進行加速。Solr對構建索引的服務進行了上層封裝,提供一個web服務的介面,可以直接通過視覺化介面對結果進行查詢。

3 解決方案

在這裡插入圖片描述 綜上,針對億級資料多條件組合查詢,給出的解決方案是使用HBase+Solr的方式,CDH將HBase和Solr都以元件的方式提供出來,可以使用CDH平臺對HBase和Solr進行統一的管理。Hbase用於儲存海量資料,Solr使用SolrCloud模式進行部署,提供索引構建和查詢。索引的建立可以通過介面離線批量建立,也可以使用HBase Indexer連線HBase和Solr,提供自動化索引構建,CDH平臺也集成了Hbase Indexer(Lily HBase Indexer)這一元件。

4 方案測試

筆者將一億條包含10個欄位的資料開啟10個執行緒插入Hbase中,然後使用Solr對10個欄位構建了索引,在Solr的視覺化介面進行查詢,查詢結果如下圖所示。 查詢全部資料 組合查詢1 組合查詢2 其中,QTime為響應時間,q為查詢語句,wt為請求格式(設定請求格式為xml響應速度更快),numFound為找到符合查詢條件的資料條數,docs為返回的資料也就是rowKey。可以看到,組合查詢都能夠在秒級響應返回響應rowKey,而通過rowKey在HBase中返回該條資料的所有欄位可以在毫秒級響應,如下圖所示: 在這裡插入圖片描述 至此,可以證明,億級資料多條件組合查詢使用HBase+Solr的解決方案能夠滿足秒級響應的需求。