這里面說(shuō)的read既包括get,也包括scan,實(shí)際底層來(lái)看這兩個(gè)操作也是一樣的。
我們將要討論的是,當(dāng)我們從一張表讀取數(shù)據(jù)的時(shí)候hbase到底是怎么處理的。
分二種情況來(lái)看,第一種就是表剛創(chuàng)建,所有put的數(shù)據(jù)還在memstore中,并沒(méi)有刷新到hdfs上;第二種情況是,該store已經(jīng)進(jìn)行多次的flush操作,產(chǎn)生了多個(gè)storefile了。
在具體說(shuō)明兩種情況前,先考慮下表的region的問(wèn)題,如果表只有一個(gè)region,那么沒(méi)有說(shuō)的,肯定是要掃描這個(gè)唯一的region。假設(shè)該表有多個(gè)region,此時(shí).META.表就派上用場(chǎng)了,hbase會(huì)首先根據(jù)你要掃描的數(shù)據(jù)的rowkey來(lái)判斷到底該數(shù)據(jù)放在哪個(gè)region上,該region所在服務(wù)器地址,然后把數(shù)據(jù)讀取的請(qǐng)求發(fā)送給該region server。好了,實(shí)際對(duì)數(shù)據(jù)訪(fǎng)問(wèn)的任務(wù)都會(huì)放在region server上執(zhí)行,為了簡(jiǎn)單起見(jiàn),接下來(lái)的討論都是在單臺(tái)region server上對(duì)單個(gè)region的操作。
首先來(lái)看第一種情況,表剛創(chuàng)建,所有put的數(shù)據(jù)還在memstore中,并沒(méi)有刷新到hdfs上。這個(gè)時(shí)候數(shù)據(jù)是在memstore中,并沒(méi)有storefile產(chǎn)生,理所當(dāng)然,hbase要查找memstore來(lái)獲得相應(yīng)的數(shù)據(jù)。對(duì)于memstore或者storefile來(lái)說(shuō),內(nèi)存中都有關(guān)于rowkey的索引的,所以對(duì)于通過(guò)rowkey的查詢(xún)速度是非常快速的。通過(guò)查詢(xún)?cè)撍饕椭朗欠翊嬖谛枰榭吹臄?shù)據(jù),已經(jīng)該數(shù)據(jù)在memstore中的位置。通過(guò)索引提供的信息就很容易找得到所需要的數(shù)據(jù)。這種情況很簡(jiǎn)單。
在來(lái)看第二種情況,該store已經(jīng)進(jìn)行多次的flush操作,產(chǎn)生了多個(gè)storefile了。那么數(shù)據(jù)應(yīng)該從哪里查呢?所有的storefile?別忘記還有memstore。此時(shí)memstore中可能還會(huì)有沒(méi)來(lái)得及flush的數(shù)據(jù)呢。如果此時(shí)該region還有很多的文件,是不是所有的文件都需要查找呢?hbase在查找先會(huì)根據(jù)時(shí)間戳或者查詢(xún)列的信息來(lái)進(jìn)行過(guò)濾,過(guò)濾掉那些肯定不含有所需數(shù)據(jù)的storefile或者memstore,盡量把我們的查詢(xún)目標(biāo)范圍縮小。
盡管縮小了,但仍可能會(huì)有多個(gè)文件需要掃描的。storefile的內(nèi)部有三維有序的,但是各個(gè)storefile之間并不是有序的。比如,storefile1中可能有rowkey為100到110的記錄,而storefile2可能有rowkey為105到115的數(shù)據(jù),storefile的rowkey的范圍很有可能有交叉。所以查詢(xún)數(shù)據(jù)的過(guò)程也不可能是對(duì)storefile的順序查找。
hbase會(huì)首先查看每個(gè)storefile的最小的rowkey,然后按照從小到大的順序進(jìn)行排序,結(jié)果放到一個(gè)隊(duì)列中,排序的算法就是按照hbase的三維順序,按照rowkey,column,ts進(jìn)行排序,rowkey和column是升序,而ts是降序。
實(shí)際上并不是所有滿(mǎn)足時(shí)間戳和列過(guò)濾的文件都會(huì)加到這個(gè)隊(duì)列中,hbase會(huì)首先對(duì)各個(gè)storefile中的數(shù)據(jù)進(jìn)行探測(cè),只會(huì)掃描掃描那些存在比當(dāng)前查詢(xún)的rowkey大的記錄的storefile。舉例來(lái)說(shuō),我當(dāng)前要查找的rowkey為108,storefile1中rowkey范圍為100~104,storefile2中rowkey的范圍為105~110,那么對(duì)于storefile1最大的rowkey為104,小于105,所以不存在比所查rowkey105大的記錄,storefile并不會(huì)被加到該隊(duì)列中。根據(jù)相同的規(guī)則,storefile2則會(huì)被添加到該隊(duì)列中。
隊(duì)列有了,下面開(kāi)始查詢(xún)數(shù)據(jù),首先通過(guò)poll取出隊(duì)列的頭storefile,會(huì)從storefile讀取一條記錄返回;接下來(lái)呢,該storefile的下條記錄并不一定是查詢(xún)結(jié)果的下一條記錄,因?yàn)殛?duì)列的比較順序是比較的每個(gè)storefile的第一條符合要求的rowkey。所以,hbase會(huì)繼續(xù)從隊(duì)列中剩下的storefile取第一條記錄,把該記錄與頭storefile的第二條記錄做比較,如果前者大,那么返回頭storefile的第二條記錄;如果后者大,則會(huì)把頭storefile放回隊(duì)列重新排序,在重新取隊(duì)列的頭storefile。然后重復(fù)上面的整個(gè)過(guò)程。這個(gè)過(guò)程比較煩,語(yǔ)言描述不清楚,代碼會(huì)更加清晰。
這段代碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
public KeyValue next() throws IOException { if(this.current == null) { return null; } KeyValue kvReturn = this.current.next(); KeyValue kvNext = this.current.peek(); if (kvNext == null) { this.current.close(); this.current = this.heap.poll(); } else { KeyValueScanner topScanner = this.heap.peek(); if (topScanner == null || this.comparator.compare(kvNext, topScanner.peek()) >= 0) { this.heap.add(this.current); this.current = this.heap.poll(); } } return kvReturn; }
|
以上的代碼在KeyValueHeap.
java類(lèi)中。
舉個(gè)例子來(lái)說(shuō)明:表sunwg01,有兩個(gè)storefile,storefile1中包括rowkey100,rowkey110;storefile2中包括rowkey104,rowkey108。我現(xiàn)在執(zhí)行scan ‘sunwg01′掃描表sunwg01中的所有的記錄。
根據(jù)前面提到的排序規(guī)則,隊(duì)列中會(huì)有2個(gè)元素,按順序分別為storefile1,storefile2。
1,取出storefile1中的第一條記錄rowkey100,并返回該結(jié)果
2,取出storefile1中的下一條記錄rowkey110,同時(shí)取出隊(duì)列剩余storefile的第一條記錄rowkey104,經(jīng)過(guò)比較rowkey110大于rowkey104,則將storefile1放回隊(duì)列中
3,因?yàn)殛?duì)列是有序的隊(duì)列,會(huì)重新對(duì)storefile進(jìn)行排序,因?yàn)榇藭r(shí)storefile1的最小rowkey為110,而storefile2的最小rowkey為104,所以排序的結(jié)果為storefile2,storefile1
4,重復(fù)上面的過(guò)程,直到查不到記錄為止。
最后查到的結(jié)果為:rowkey100,rowkey104,rowkey108,rowkey110。
順便說(shuō)下block cache的事情,當(dāng)從storefile中讀數(shù)據(jù)的時(shí)候會(huì)首先查看block cache中是否有該數(shù)據(jù),如果有則直接查block cache,就沒(méi)必要查詢(xún)hdfs;如果沒(méi)有該數(shù)據(jù),那么就只能去查hdfs了。這也是為了block cache的命中率對(duì)性能有很大影響的原因。
上面描述了從hbase中read的基本的過(guò)程,還有些細(xì)節(jié)沒(méi)有具體說(shuō),但是大概過(guò)程應(yīng)該是都說(shuō)到了。