朱松杰,婁淵勝,葉 楓,,李 凌,陳 勇
1.河海大學(xué) 計(jì)算機(jī)與信息學(xué)院,南京211100
2.南京龍淵微電子科技有限公司 博后工作站,南京211106
隨著大數(shù)據(jù)時(shí)代的到來,傳統(tǒng)的關(guān)系型數(shù)據(jù)庫難以處理無規(guī)范模式的數(shù)據(jù)集,并且隨著數(shù)據(jù)集規(guī)模的增大,不能提供高效的存儲(chǔ)和查詢服務(wù),也不能滿足系統(tǒng)的新需求?;ヂ?lián)網(wǎng)先驅(qū)不得不重新設(shè)計(jì)數(shù)據(jù)庫,大數(shù)據(jù)系統(tǒng)和NoSQL(Not-Only-SQL即非關(guān)系型數(shù)據(jù)庫)越來越多地被開發(fā)出來,例如Facebook的Cassandra、Amazon的Dynamo 以及支持高效數(shù)據(jù)查詢的內(nèi)存數(shù)據(jù)存儲(chǔ)系統(tǒng)Redis等[1]。這些數(shù)據(jù)庫的數(shù)據(jù)存儲(chǔ)都采用了key-value數(shù)據(jù)模型,HBase 便是key-value 數(shù)據(jù)庫中使用最為廣泛的[2]。
在Google 的BigTable 論文的發(fā)表后,Cafarella 在Hadoop 上面實(shí)現(xiàn)了BigTable 的一個(gè)開源版本,被稱為HBase[3]。HBase由多個(gè)軟件子系統(tǒng)組成,主要包括客戶端、HMaster、HRegionServer、Zookeeper 等,這些子系統(tǒng)共同組成一個(gè)分布式應(yīng)用系統(tǒng),它具有開源、分布式、可擴(kuò)展及面向列存儲(chǔ)的特點(diǎn),能夠?yàn)榇髷?shù)據(jù)提供隨機(jī)、實(shí)時(shí)的讀寫訪問功能。如何從海量數(shù)據(jù)中快速獲得所需數(shù)據(jù)是使用HBase 等NoSQL 數(shù)據(jù)庫的一個(gè)重要原因,HBase在其主鍵上建立了B+樹索引,在使用主鍵進(jìn)行查詢時(shí)效率很高[4]。但是,在進(jìn)行非主鍵的條件查詢時(shí),由于缺少主鍵的支撐,HBase 必須進(jìn)行全表掃描,導(dǎo)致查詢效率低下,無法滿足上述要求。對(duì)此,本文在借鑒關(guān)系型數(shù)據(jù)庫解決方案的基礎(chǔ)上,提出了一種基于二級(jí)索引的查詢機(jī)制,并將索引數(shù)據(jù)存儲(chǔ)在內(nèi)存數(shù)據(jù)庫中,通過內(nèi)存進(jìn)行二級(jí)索引檢索,進(jìn)一步提高了對(duì)大數(shù)據(jù)的實(shí)時(shí)查詢響應(yīng)。
HBase 是一個(gè)構(gòu)建在HDFS 之上,用于海量數(shù)據(jù)存儲(chǔ)分布式列存儲(chǔ)系統(tǒng)[5]。HBase 表的每行都是按照RowKey 的字典序排序存儲(chǔ),每行數(shù)據(jù)是按照RowKey區(qū)間進(jìn)行分割存儲(chǔ)成多個(gè)Region。HBase 只針對(duì)行健的索引,在原生產(chǎn)品中訪問HBase表中的行只能通過行健訪問,行健區(qū)間訪問和全表掃描三種。
當(dāng)前,國內(nèi)許多使用HBase的企業(yè)或者個(gè)人都會(huì)對(duì)其建立索引來提高性能,比較常見的有基于Coprocessor方案,如華為的HIndex方案和Apache Phoenix方案[6]。其支持自定義數(shù)據(jù)處理邏輯,采用數(shù)據(jù)“雙寫”(dual-write)策略,在有數(shù)據(jù)寫入時(shí)同時(shí)同步到二級(jí)索引表。相比于華為的HIndex,Apache Phoenix 的版本迭代情況較好,支持和兼容多個(gè)HBase版本,二級(jí)索引只是其中一塊功能[6]。同時(shí),二級(jí)索引的創(chuàng)建和管理直接有SQL語法的支持,使用簡(jiǎn)單。王文賢等人[7]實(shí)現(xiàn)了一種基于Solr 的HBase海量數(shù)據(jù)二級(jí)索引方案,該方案主要對(duì)華為Hindex進(jìn)行改進(jìn),其基于數(shù)據(jù)存儲(chǔ)和索引分離的思想,將Solr 檢索與HBase 的大數(shù)據(jù)存儲(chǔ)結(jié)合起來,具有一定的通用性。丁飛等人[4]實(shí)現(xiàn)了基于協(xié)處理器的HBase區(qū)域級(jí)服務(wù)端區(qū)域級(jí)第二索引擴(kuò)展功能,索引存儲(chǔ)格式選HBase自身的數(shù)據(jù)組織方式,即HFile文件格式。利用HFile高效的IO性能保證索引查詢的效率。周偉等人[8]對(duì)HBase分布式二級(jí)索引通用方案進(jìn)行研究,引入分布式索引機(jī)制,在SolrCloud中完成對(duì)索引的管理,借助協(xié)處理器提供的索引功能為HBase記錄創(chuàng)建、存儲(chǔ)索引,其中HBase負(fù)責(zé)存儲(chǔ)數(shù)據(jù),Solr負(fù)責(zé)索引數(shù)據(jù)和檢索。
許多研究者已經(jīng)使用Coprocessor 來構(gòu)建HBase 二級(jí)索引,具體有兩種解決方案:一種是直接存儲(chǔ)到數(shù)據(jù)庫或文件中實(shí)現(xiàn)索引持久化,但這種方法的索引檢索效率較低,另一種是通過構(gòu)建內(nèi)存索引,并存儲(chǔ)到特定環(huán)境中實(shí)現(xiàn)持久化。從開發(fā)設(shè)計(jì)的角度看,把很多對(duì)二級(jí)索引管理的細(xì)節(jié)都封裝在的Coprocessor具體實(shí)現(xiàn)類里面,這些細(xì)節(jié)對(duì)外面讀寫的人是無感知的,簡(jiǎn)化了數(shù)據(jù)訪問者的使用。雖然具有一定的入侵性,會(huì)對(duì)Region-Server性能產(chǎn)生一定影響。
對(duì)于非Coprocessor方案,不基于Coprocessor開發(fā),而是自行在外部構(gòu)建和維護(hù)索引關(guān)系。常見的是采用底層基于Apache Lucene 的Elasticsearch[9],來構(gòu)建強(qiáng)大的索引能力、搜索能力,例如支持模糊查詢、全文檢索、組合查詢、排序等。LilyHBase Indexer(也簡(jiǎn)稱HBase Indexer)是國外的NGDATA公司開源的基于solr的索引構(gòu)建工具,特色是其基于HBase 的備份機(jī)制,開發(fā)了一個(gè)叫SEP工具,通過監(jiān)控HBase的WAL日志(Put/Delete操作),來觸發(fā)對(duì)solr 集群索引的異步更新,基本對(duì)HBase無侵入性[10]。葛微等人[3]提出了名為HiBase的二級(jí)索引,它是一種基于分層式索引的設(shè)計(jì)方案,其將熱點(diǎn)索引進(jìn)行緩存,并建立高效的緩存替換策略來提高二級(jí)索引的查詢速度。Zou 等提出了互補(bǔ)聚簇式索引(Complemental Clustering Index,CCIndex),該方案把數(shù)據(jù)的詳細(xì)信息也存放在索引表中,不需要通過獲取的行鍵再到原表中去查找數(shù)據(jù)。使用非Coprocessor方案時(shí),如存儲(chǔ)開銷比較大,尤其是當(dāng)索引列比較多的時(shí)候,空間開銷會(huì)更大。索引更新代價(jià)比較高,會(huì)影響系統(tǒng)的吞吐量,索引創(chuàng)建以后,不能夠動(dòng)態(tài)增加或修改。
綜上,本文使用基于觀察者模式的Coprocessor 方法實(shí)現(xiàn)二級(jí)索引功能,并通過分布式存儲(chǔ)保證二級(jí)索引的高可用和高容錯(cuò)性。提出了基于Coprocessor的索引構(gòu)建方案,并且針對(duì)協(xié)處理器索引檢索速度問題,構(gòu)建了內(nèi)存索引,意圖是提高索引檢索速度。
所提機(jī)制的總體結(jié)構(gòu)如圖1所示。其中,索引管理模塊是框架核心,在用戶對(duì)數(shù)據(jù)表進(jìn)行更新操作時(shí),協(xié)處理器Coprocessor 會(huì)對(duì)這些請(qǐng)求進(jìn)行攔截,并對(duì)索引表進(jìn)行相應(yīng)操作,包括插入、刪除和更新索引的元數(shù)據(jù)(記錄用戶表對(duì)應(yīng)的索引表名稱、索引列等信息)。執(zhí)行查詢操作時(shí),系統(tǒng)會(huì)在緩存中尋找對(duì)應(yīng)索引位置,提高了索引檢索速度,當(dāng)緩存中的索引更新時(shí),索引管理模塊也會(huì)對(duì)索引進(jìn)行更新。索引持久化管理模塊主要對(duì)緩存索引進(jìn)行持久化操作,提供索引表和值表的持久化存儲(chǔ),HBase為持久化存儲(chǔ)數(shù)據(jù)提供可擴(kuò)展性和容錯(cuò)性。
圖1 系統(tǒng)總體結(jié)構(gòu)圖
由于HBase無法定制服務(wù)端邏輯,使得用戶無法在服務(wù)端實(shí)現(xiàn)自己需求的二級(jí)索引方案,HBase在0.92版本中引入了協(xié)處理器Coprocessor 機(jī)制,它受啟發(fā)于Google 的Bigtable 的協(xié)處理器,為實(shí)現(xiàn)建立二次索引、復(fù)雜過濾器(謂詞下推)以及訪問控制等提供了一種很好的解決方案[11]。協(xié)處理器是一種服務(wù)端組件,類似于輕量級(jí)的MapReduce,它的主要思想是通過將集群內(nèi)的數(shù)據(jù)移動(dòng)取代為計(jì)算移動(dòng)來減少計(jì)算代價(jià),提高效率。其可理解為服務(wù)端的攔截器,可根據(jù)需求確定攔截點(diǎn),再重寫這些攔截點(diǎn)對(duì)應(yīng)的方法。協(xié)處理器允許在Region服務(wù)器上運(yùn)行自己的代碼,更準(zhǔn)確地說是允許用戶執(zhí)行Region級(jí)的操作,并且可以使用與RDBMS中觸發(fā)器(trigger)類似的功能。
Coprocessor可以分為兩大類:Observer Coprocessors(觀察者)和EndPoint Coprocessor(終端)。Observer Coprocessor在一個(gè)特定的事件發(fā)生前或發(fā)生后觸發(fā),其工作過程如圖2所示。
圖2 協(xié)處理器工作過程
Observer Coprocessor使用場(chǎng)景如下。
(1)安全性:在執(zhí)行Get 或Put 操作前,通過preGet或prePut方法檢查是否允許該操作。
(2)引用完整性約束:HBase 并不直接支持關(guān)系型數(shù)據(jù)庫中的引用完整性約束概念,即通常所說的外鍵。但是,可以使用Coprocessor增強(qiáng)這種約束。
(3)二級(jí)索引:可以使用Coprocessor 來維持一個(gè)二級(jí)索引。
Endpoint協(xié)處理器類似傳統(tǒng)數(shù)據(jù)庫中的存儲(chǔ)過程,客戶端可以調(diào)用這些Endpoint協(xié)處理器執(zhí)行一段Server端代碼,并將Server 端代碼的結(jié)果返回給客戶端進(jìn)一步處理[12]。利用Coprocessor,用戶可以將代碼部署到HBase Server 端,HBase 將利用底層cluster 的多個(gè)節(jié)點(diǎn)并發(fā)執(zhí)行。Endpoint 不僅能與客戶端通信,而且能與Observer 進(jìn)行通信。Observer 可以在回調(diào)方法里通知Endpoint,執(zhí)行一定的事件響應(yīng)邏輯。兩種處理器的共同協(xié)作可以定制功能強(qiáng)大的HBase應(yīng)用,既可以實(shí)現(xiàn)對(duì)集群中每個(gè)Region數(shù)據(jù)變更的監(jiān)控,又可以通過Endpoint將處理結(jié)果返回給客戶端。
協(xié)處理器機(jī)制支持用戶根據(jù)業(yè)務(wù)要求,重寫Endpoint和Observer代碼,本文使用Observer協(xié)處理器來進(jìn)行索引的構(gòu)建。Observer 協(xié)處理器類似于關(guān)系型數(shù)據(jù)庫中的觸發(fā)器,當(dāng)對(duì)數(shù)據(jù)庫進(jìn)行增刪改查操作時(shí),Observer會(huì)對(duì)這些請(qǐng)求進(jìn)行攔截,并根據(jù)這些請(qǐng)求,實(shí)時(shí)更新索引表。二級(jí)索引的整體架構(gòu)圖如圖3所示。
圖3 二級(jí)索引架構(gòu)圖
建立的索引結(jié)構(gòu)如圖3 所示,采用了倒排的方法,將數(shù)據(jù)表的值與主鍵進(jìn)行交換,原本的值充當(dāng)主鍵,原本的主鍵放在值的位置,一個(gè)最基本的索引表即構(gòu)建完畢。在未建立索引前,HBase的數(shù)據(jù)模型可形式化表示為:
其中行健為R,列族為C ,列限定符為CQ,時(shí)間戳為T ,值為V 。則二級(jí)索引的形式化表示為:Vi→{Ci→{CQi→{Ti|R}}},i=0,1,2,…,n。
框架保證了索引文件和主表在同一個(gè)Regionserver上,這樣可以保證在需要使用索引文件時(shí)只需與Region-Server建立一次連接就可以完成,提高了速度。在此架構(gòu)中,用戶首先在配置文件(hbase-site.xml)中設(shè)定索引的細(xì)節(jié),主服務(wù)器從配置文件中獲取需要建立的索引信息,然后在對(duì)應(yīng)的RegionServer中的IndexingCoprocessor中建立索引同時(shí)管理二級(jí)索引數(shù)據(jù)。每個(gè)節(jié)點(diǎn)上都部署了協(xié)處理器Coprocessor,部署之后RegionServer端中的每個(gè)區(qū)域Region 上都會(huì)自動(dòng)創(chuàng)建一個(gè)區(qū)域協(xié)處理RegionCoprocessorHost實(shí)例,它的主要功能是用來維護(hù)系統(tǒng)級(jí)或表級(jí)的區(qū)域觀察者協(xié)處理RegionObserver。每當(dāng)RegionObserver啟動(dòng)時(shí)會(huì)將RegionCoprocessorHost初始化,初始化的過程中,RegionCoprocessorHost 會(huì)加載當(dāng)前服務(wù)端所有的RegionObserver。索引管理由擴(kuò)展的RegionObserver 實(shí)例(IndexRegionObserver)完成。IndexRegionObserver主要的擴(kuò)展接口如表1所示。
表1 IndexRegionObserver主要擴(kuò)展接口
下面以Put攔截器舉例說明索引表的更新過程:
如圖4所示,在數(shù)據(jù)表進(jìn)行Put操作時(shí),數(shù)據(jù)表會(huì)監(jiān)測(cè)插入數(shù)據(jù)的Rowkey,并由此定位到RegionServer 上的Region,該事件在Put 操作之前被CoprocessorHost攔截,并自動(dòng)調(diào)用此Region 上進(jìn)行監(jiān)聽的Observer 的prePut 方法。用戶可以根據(jù)自己的需求對(duì)這個(gè)方法重寫,更新索引。在索引更新完成后,即Put方法執(zhí)行完成后,Put之后的事件會(huì)再次被CoprocessorHost攔截,并調(diào)用監(jiān)聽器Observer 的postPut 方法,同樣,該方法支持用戶的重寫,以實(shí)現(xiàn)在Put 操作完成后的索引邏輯。由此可以看出,在Put操作未完成時(shí),監(jiān)聽器將監(jiān)聽到這一事件,從而不會(huì)調(diào)用postPut 方法,索引邏輯將無法完成,保證了索引與原數(shù)據(jù)表的一致性與事件性,不會(huì)出現(xiàn)索引與原數(shù)據(jù)表無法匹配的情況。
圖4 索引表更新過程
索引的使用:在構(gòu)建完索引表后,當(dāng)進(jìn)行索引查詢時(shí),客戶端需要進(jìn)行Scan操作,此操作將會(huì)被協(xié)處理器攔截,將檢索條件截取,并從索引表中找出符合條件的索引項(xiàng),返回原數(shù)據(jù)表的Rowkey,數(shù)據(jù)表根據(jù)Rowkey查詢即可。
Coprocessor 構(gòu)建的索引只是在邏輯上實(shí)現(xiàn)了索引結(jié)構(gòu),為了提高檢索Coprocessor 索引速度,需要對(duì)Coprocessor 構(gòu)建的索引進(jìn)行特定結(jié)構(gòu)設(shè)計(jì),內(nèi)存索引設(shè)計(jì)即可完成上述目標(biāo)。內(nèi)存的索引相較于傳統(tǒng)位于磁盤的索引在設(shè)計(jì)和架構(gòu)上都大不相同,基于內(nèi)存的索引在查詢效率上得到了極大提升。廣泛采用的內(nèi)存索引有T 樹、基于緩存敏感的CSS/CSB+樹和改進(jìn)的Hash 索引等[13]。使用了HT 樹構(gòu)建內(nèi)存索引,其結(jié)構(gòu)如圖5所示。
圖5 HT樹的結(jié)構(gòu)
如圖5所示,每個(gè)葉子節(jié)點(diǎn)有4個(gè)哈希表,每個(gè)哈希表中有3個(gè)哈希桶,在使用HT構(gòu)建內(nèi)存索引時(shí),需要通過查找算法查找到關(guān)鍵字可以插入的哈希表,然后通過計(jì)算,找到關(guān)鍵字可插入的哈希桶,判斷哈希桶是否已滿,若已滿則分裂該節(jié)點(diǎn),將該關(guān)鍵字插入,若未滿則直接插入哈希桶。
內(nèi)存索引工作流程與一般索引的使用過程有所不同,具體流程如下:
(1)內(nèi)存索引初始化
當(dāng)向數(shù)據(jù)表的構(gòu)建過程中,需要同步進(jìn)行內(nèi)存索引的建立,對(duì)外提供檢索服務(wù)。在第一次請(qǐng)求索引檢索時(shí),系統(tǒng)會(huì)檢查內(nèi)存索引是否為空,如果為空,則進(jìn)行索引的初始化操作,建立內(nèi)存索引。
(2)Put操作過程
Put 操作在HBase 中相當(dāng)于插入操作,當(dāng)HBase 執(zhí)行這個(gè)操作時(shí),當(dāng)前操作發(fā)生的Region上的Observer會(huì)攔截這個(gè)事件,向內(nèi)存索引中插入一條對(duì)應(yīng)的索引項(xiàng)。
(3)Delete操作過程
和Put 操作相似,當(dāng)客戶端執(zhí)行Delete 操作時(shí),HBase將從表中刪除一條記錄,當(dāng)前操作發(fā)生的Region上的Observer會(huì)攔截這個(gè)事件,在內(nèi)存索引中刪除一條對(duì)應(yīng)的索引項(xiàng)。
(4)查詢操作過程
該機(jī)制中,為了提高查詢效率,盡量將數(shù)據(jù)處理過程本地化。在協(xié)處理器攔截到查詢請(qǐng)求后,將會(huì)構(gòu)建檢索條件,根據(jù)條件在內(nèi)存索引中進(jìn)行多線程檢索。得到滿足檢索條件的Rowkey 后,返回?cái)?shù)據(jù)表中查詢?cè)紨?shù)據(jù),將結(jié)果返回客戶端。在內(nèi)存中檢索的過程如下:
在進(jìn)行內(nèi)存索引查詢時(shí),首先在哈希表中進(jìn)行查找,定位關(guān)鍵字所在桶,繼續(xù)在桶中查找,若找到,則指針指向所需記錄。當(dāng)關(guān)鍵字處于內(nèi)部節(jié)點(diǎn),且它的鍵為K1,K2,…,Kn。如果key <K1,則為第一個(gè)子節(jié)點(diǎn);如果K1≤key <K2,則為第二個(gè)子節(jié)點(diǎn)。依此類推,在該子節(jié)點(diǎn)上遞歸運(yùn)用查找過程。對(duì)于范圍查詢,在實(shí)際情況下使用頻率較高,下面給出算法進(jìn)行說明。
算法1 內(nèi)存索引查找算法
輸入 查詢范圍[ks,ke]的關(guān)鍵字ks,ke。輸出 行鍵集合RQ。
RQ ←[?]// 結(jié)果集合設(shè)為空
Hs←T .Get Hash(ks)//通過樹形索引查找ks所在的哈希表
He←T .Get Hash(ke)
H ←Hs
while next[H]≠Hsdo
H ←next[H]
RQ ∪all Value[H] //將哈希表中所有值并入結(jié)果集合中
end while
for each map M in Hsdo //遍歷哈希表
if key [M]>ksthen //當(dāng)遍歷映射的鍵大于范圍開始的關(guān)鍵字時(shí)
RQ ∪value[M] //將映射的值并入結(jié)果集合中
end if
end for
for each map M in Hedo if key [M]<kethen //當(dāng)遍歷映射的鍵小于范圍結(jié)束的關(guān)鍵字時(shí)
RQ ∪value[M]
end if
end for
end for
(5)Region分片過程
Region分片主要為了解決在數(shù)據(jù)表數(shù)據(jù)增加時(shí),數(shù)據(jù)行超出預(yù)設(shè)分片大小的情況。當(dāng)數(shù)據(jù)表發(fā)生分片過程即Split操作時(shí),對(duì)應(yīng)的內(nèi)存索引對(duì)象也會(huì)隨之發(fā)生變化[14]。分片時(shí),Region 將會(huì)從一個(gè)變?yōu)閮蓚€(gè),數(shù)據(jù)表的也會(huì)一分為二,分別存于兩個(gè)Region 中,此時(shí)Observer會(huì)監(jiān)測(cè)到Split事件,通知Endpoint重新構(gòu)建索引表。如圖6所示。
圖6 分片時(shí)表變化
數(shù)據(jù)表從第三項(xiàng)分裂成兩張表,協(xié)處理器在原索引表第一行構(gòu)建索引游標(biāo),以此標(biāo)記向后檢索并在Region的數(shù)據(jù)表中尋找是否有此條記錄,若存在則保留數(shù)據(jù)記錄,否則刪除,索引表由此更新完成。內(nèi)存索引構(gòu)建在索引表主鍵之上,由于索引主鍵結(jié)構(gòu)的改變,原索引結(jié)構(gòu)需要初始化操作,即步驟一操作。新分片的內(nèi)存索引對(duì)象存儲(chǔ)在新的Region協(xié)處理器中。
由于內(nèi)存索引的維護(hù)都由協(xié)處理器完成,因此,內(nèi)存持久化的操作主要為了保證內(nèi)外存數(shù)據(jù)的一致性,保證主機(jī)斷電時(shí)不用重新構(gòu)建索引,不涉及對(duì)索引創(chuàng)建操作。提出了內(nèi)存索引持久化方法,將建立的內(nèi)存索引從內(nèi)存中映射到外存中,用于持久化內(nèi)存索引的外存區(qū)域索引文件由存儲(chǔ)索引信息的索引頭和索引結(jié)構(gòu)組成,索引文件與內(nèi)存空間采用內(nèi)存映射技術(shù)mmap 保持一致性[15],映射時(shí)會(huì)使用地址轉(zhuǎn)換表,將索引文件中的地址轉(zhuǎn)換為內(nèi)存中地址,并一一對(duì)應(yīng)。在對(duì)內(nèi)存索引結(jié)構(gòu)進(jìn)行修改時(shí),外存索引文件也會(huì)自動(dòng)更新。具體的內(nèi)存持久化流程如圖7所示。
圖7 內(nèi)存持久化流程
在上述流程圖中,一個(gè)重要的步驟即如何將索引文件映射入內(nèi)存,索引文件映射入內(nèi)存后返回的是虛擬地址,索引結(jié)構(gòu)數(shù)據(jù)沒有進(jìn)入內(nèi)存中。為了保證索引結(jié)構(gòu)完全處于內(nèi)存中,要使用相應(yīng)的方法將索引結(jié)構(gòu)逐步導(dǎo)入內(nèi)存,采用了層次遍歷的方法,將索引樹完全遍歷,所有索引結(jié)構(gòu)便處于內(nèi)存中。
為評(píng)估本文提出的基于協(xié)處理器的內(nèi)存索引方案,驗(yàn)證在構(gòu)建二級(jí)索引時(shí)對(duì)數(shù)據(jù)庫寫入性能的影響和在構(gòu)建了二級(jí)索引時(shí)對(duì)查詢性能有怎樣的提升。同時(shí),實(shí)驗(yàn)同樣基于協(xié)處理器但使用內(nèi)存構(gòu)建索引(本文方案)與使用solr構(gòu)建索引在查詢性能上的表現(xiàn),測(cè)試了數(shù)據(jù)擴(kuò)展性與集群擴(kuò)展性。本文在主機(jī)上搭建了Hadoop集群、Zookeeper集群和HBase集群,為了與同樣基于協(xié)處理器的solr 方案和HiBase 方案進(jìn)行比較,還搭建了solr集群。實(shí)驗(yàn)環(huán)境如表2所示。
表2 實(shí)驗(yàn)環(huán)境
實(shí)驗(yàn)所用數(shù)據(jù)為某河流近3 年各站點(diǎn)水位觀測(cè)記錄,屬性包括ID、站點(diǎn)(STCD)、水位(RZ)、地區(qū)(RFROM)和時(shí)間(TM),共3 000萬條數(shù)據(jù)。
實(shí)驗(yàn)在分布式環(huán)境下進(jìn)行,通過3臺(tái)客戶端同時(shí)向HBase 表中插入數(shù)據(jù),并在3 臺(tái)客戶端上統(tǒng)計(jì)每500 萬數(shù)據(jù)的Put 時(shí)間,為了保證實(shí)驗(yàn)的準(zhǔn)確性,進(jìn)行30 次重復(fù)測(cè)試,并將結(jié)果累加求得均值。實(shí)驗(yàn)分別測(cè)試了無索引、基于solr 的索引、HiBase 和本文提出的基于協(xié)處理器與內(nèi)存的索引方案在相同條件下的Put 時(shí)間,結(jié)果如圖8所示。
圖8 插入實(shí)驗(yàn)結(jié)果
從實(shí)驗(yàn)結(jié)果可以看出,Put 效率執(zhí)行最高的是無索引方案,這是因?yàn)樵谶M(jìn)行Put操作時(shí),無索引方案無需分配資源進(jìn)行索引的構(gòu)建,而其他需構(gòu)建二級(jí)索引的方案在將數(shù)據(jù)放入數(shù)據(jù)表的同時(shí),會(huì)觸發(fā)協(xié)處理器對(duì)Put 操作進(jìn)行攔截,并調(diào)用用戶自定義的方法開始構(gòu)建二級(jí)索引。同時(shí)也可得出,在前對(duì)500 萬條數(shù)據(jù)進(jìn)行Put 操作時(shí),各方案的性能差距較小,但在到達(dá)1 000 萬數(shù)量級(jí)時(shí),無索引的方案明顯優(yōu)于其他有索引方案。這是因?yàn)殡S著數(shù)據(jù)量的增加,索引數(shù)據(jù)也越來越多,在進(jìn)行索引插入時(shí)也會(huì)相較500 萬條記錄時(shí)更加困難。HiBase 和本方案都是基于內(nèi)存索引,在構(gòu)建內(nèi)存索引時(shí),需要在協(xié)處理器中構(gòu)建適合內(nèi)存存儲(chǔ)的索引結(jié)構(gòu),會(huì)消耗額外的計(jì)算資源。而基于solr的方案只需要在solr中構(gòu)建二級(jí)索引,不需要在程序中進(jìn)行額外計(jì)算。因此,HiBase和本方案相較基于solr的方案稍有劣勢(shì),但在一個(gè)可接受的范圍之內(nèi)。
實(shí)驗(yàn)在一臺(tái)客戶端上進(jìn)行,數(shù)據(jù)源仍然是3 000 萬條數(shù)據(jù),本次實(shí)驗(yàn)設(shè)置了4 個(gè)測(cè)試用例。詳情如下:查詢河流站點(diǎn)水位值(STCD)在一定范圍內(nèi)的記錄條數(shù),對(duì)水位值范圍更改后即可得到不同的數(shù)據(jù)量。在水位值屬性上以建立HT 樹內(nèi)存索引,對(duì)水位值進(jìn)行范圍查詢,圖9 所示橫坐標(biāo)數(shù)據(jù)是4 次測(cè)試用例所查詢到的所有結(jié)果數(shù)據(jù)量。進(jìn)行30 次測(cè)試,求得各數(shù)據(jù)庫查詢時(shí)間的平均值,結(jié)果如圖9所示。
圖9 查詢實(shí)驗(yàn)結(jié)果
如圖9所示,無索引的方案與其他有索引的方案性能相比有很大差距,原因是,在原生的數(shù)據(jù)庫中搜尋一個(gè)特定值時(shí),只能通過Scan的方式進(jìn)行全表的掃描,效率極低,在數(shù)據(jù)量大時(shí),這種查詢方式可達(dá)幾十個(gè)小時(shí),無法進(jìn)行實(shí)際使用。同時(shí),可以發(fā)現(xiàn),無論查詢出的結(jié)果為多少,無索引的查詢速度都在110 min 左右。排除誤差影響,這是因?yàn)椋瑹o論結(jié)果如何,無索引的方案都要全表掃描,在每次掃描的數(shù)據(jù)量相同時(shí),查詢時(shí)間變化不大。而本方案相比于同樣構(gòu)建了二級(jí)索引的基于solr的方案,性能有較明顯的提升,數(shù)據(jù)量大時(shí),查詢速度為其3.5 倍左右,與未構(gòu)建索引的查詢效率相比速度是其50 倍左右,與同樣基于內(nèi)存索引的HiBase 方案提升了10%左右。本方案不僅在性能上優(yōu)勢(shì)巨大,隨著查詢結(jié)果的增大時(shí),查詢所需的時(shí)間變化也較小,因?yàn)橥ㄟ^內(nèi)存進(jìn)行計(jì)算,計(jì)算速度快,并且都在索引上建立了索引樹結(jié)構(gòu),在關(guān)鍵字搜索時(shí)根據(jù)樹結(jié)構(gòu)搜索可以快速定位,數(shù)據(jù)量增大時(shí),對(duì)速度影響也較小。
測(cè)試了基于協(xié)處理器的內(nèi)存索引在不同規(guī)模數(shù)據(jù)下均勻與非均勻分布數(shù)據(jù)集查詢性能。均勻分布數(shù)據(jù)集使用3 000 萬條水文數(shù)據(jù),單值查詢河流站點(diǎn)水位值(STCD)為62.20和范圍查詢水位值在60到65之間的記錄條數(shù),結(jié)果如圖10所示。
圖10 均勻數(shù)據(jù)集查詢性能
可以看到,當(dāng)數(shù)據(jù)的規(guī)模從0 持續(xù)變化到3 000 萬級(jí)別時(shí),查詢的時(shí)間保持了線性增長(zhǎng),這也驗(yàn)證了基于內(nèi)存方案在數(shù)據(jù)的可擴(kuò)展性方面有著良好的表現(xiàn),查詢響應(yīng)時(shí)間和結(jié)果集大小成正比。因?yàn)樵诓樵冺憫?yīng)時(shí)間中,主要開銷是對(duì)查詢結(jié)果集的訪問,在數(shù)據(jù)集中,數(shù)據(jù)是符合均勻分布的,隨著數(shù)據(jù)行總數(shù)的增長(zhǎng),查詢結(jié)果集也隨之增加,因此,查詢響應(yīng)時(shí)間會(huì)與數(shù)據(jù)行總數(shù)大小成正比。
同時(shí),本文選用了UCI數(shù)據(jù)集Abalone數(shù)據(jù)集,該數(shù)據(jù)集符合非均勻分布,數(shù)據(jù)集可分為6類,并滿足22.69%、27.53%、25.33%、19.46%、2.31%和2.68%的數(shù)據(jù)分布規(guī)律,同時(shí)長(zhǎng)度數(shù)據(jù)也滿足非均勻分布性。為了滿足大數(shù)據(jù)測(cè)試要求,將數(shù)據(jù)量以同等倍數(shù)增加,保證數(shù)據(jù)分布的不變性,查詢數(shù)據(jù)集中Length 為130 和Length 大于110小于130的數(shù)據(jù),兩種查詢結(jié)果如圖11所示。
圖11 非均勻數(shù)據(jù)集查詢性能
由圖11 可以看出,當(dāng)數(shù)據(jù)量從0 增加到600 萬時(shí),由于數(shù)據(jù)的非均勻分布性,并未出現(xiàn)像均勻分布數(shù)據(jù)集中的線性增長(zhǎng)趨勢(shì)。這是由于單值查詢和范圍查詢查詢的目標(biāo)數(shù)值普遍分布于數(shù)據(jù)表的前半段,在查詢開始時(shí)查詢所得結(jié)果集較多,因此,查詢時(shí)間增加較快。查詢數(shù)據(jù)量增多時(shí),滿足查詢條件的記錄條數(shù)減少,查詢?cè)黾訒r(shí)間也隨之變少,這也驗(yàn)證了內(nèi)存索引方案良好的可擴(kuò)展性。
同時(shí),在3 000萬條數(shù)據(jù)的基礎(chǔ)上,通過增加節(jié)點(diǎn)的數(shù)量進(jìn)行集群擴(kuò)展性實(shí)驗(yàn),由于節(jié)點(diǎn)的變化對(duì)單值查詢的性能影響可以忽略。因此,實(shí)驗(yàn)只對(duì)范圍查詢進(jìn)行,實(shí)驗(yàn)結(jié)果如圖12所示。
圖12 節(jié)點(diǎn)數(shù)量變化對(duì)查詢時(shí)間影響
可以看出,隨著節(jié)點(diǎn)數(shù)量的增加,查詢響應(yīng)時(shí)間逐漸減少。在進(jìn)行范圍查詢時(shí),會(huì)向所有節(jié)點(diǎn)發(fā)送查詢請(qǐng)求,因此,當(dāng)節(jié)點(diǎn)數(shù)量增加時(shí),相應(yīng)的查詢時(shí)間就會(huì)變短。
為了提高HBase的查詢性能,本文提出了基于協(xié)處理器Coprocessor的方案,實(shí)現(xiàn)了內(nèi)存索引的構(gòu)建,并將構(gòu)建的內(nèi)存索引持久化存儲(chǔ)在外存中,通過實(shí)驗(yàn)達(dá)到了預(yù)期目標(biāo)。其核心的思想為:通過協(xié)處理器構(gòu)建內(nèi)存索引,具體的內(nèi)存索引結(jié)構(gòu)為HT樹索引,它可以極大提高索引的檢索速度。同時(shí),使數(shù)據(jù)表與索引文件協(xié)同分布,托管于同一臺(tái)Region Server,保證了數(shù)據(jù)表和索引文件同時(shí)分布于集群中的同一臺(tái)服務(wù)器上,在需要用到索引文件時(shí),直接將索引文件映射入內(nèi)存,節(jié)約了時(shí)間。
但本文還有一些不足,為了查詢的簡(jiǎn)易性,對(duì)每一個(gè)列值都創(chuàng)建了索引,這導(dǎo)致了構(gòu)建的索引文件較為龐大,降低了索引效率。同時(shí),在內(nèi)存中構(gòu)建索引對(duì)象需要付出巨大的內(nèi)存開銷,當(dāng)數(shù)量過大或索引內(nèi)容過多時(shí),集群中各服務(wù)器的內(nèi)存會(huì)急劇消耗,有可能出現(xiàn)系統(tǒng)癱瘓。
綜上所述,本文提出的索引方案實(shí)現(xiàn)了HBase檢索速度的提高,性能和穩(wěn)定性也得到了充分的保證,但面對(duì)大數(shù)據(jù)環(huán)境下各種各樣的挑戰(zhàn),仍需要更多的努力去完善HBase的大數(shù)據(jù)處理方案。