免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
HBase權(quán)威指南中文版第三章 | CUCmehp的博客
HBase權(quán)威指南(中文版)——第三章(第一部分)
六15
byadmin on 2013 年 6 月 15 日at 下午 5:35 Posted In:HBase,文檔翻譯
第三章 客戶端API: 基礎(chǔ)篇
本章將介紹HBase客戶端API,首先要說明的是,HBase雖然是用Java開發(fā)的,其原生的API采用了Java,但它也支持其它的編程語言,在第6章會(huì)詳細(xì)的介紹如何使用其它的編程語言。
概述
操作HBase最主要的API放在源碼的org.apache.hadoop.hbase.client包下,它提供給用戶訪問(讀、寫、刪除等)HBase數(shù)據(jù)庫(kù)的功能性接口。在詳細(xì)列舉Hbase令人眼花繚亂的接口之前,我們不妨先整體上看看它的使用情況。
HBase以每個(gè)row為單元保證數(shù)據(jù)操作的原子性,這一點(diǎn)保證了所有讀寫線程對(duì)同一個(gè)row的數(shù)據(jù)訪問的一致性。換句話說,對(duì)于某個(gè)線程來說,它并不在意別的線程是否在讀、寫這一條記錄:它或者取到一個(gè)最后修改的一致性版本,或者在接受最終被修改的數(shù)據(jù)前一直被掛起,在第8章將詳細(xì)介紹這一點(diǎn)。
在正常的操作和負(fù)載下,一個(gè)讀取客戶端不會(huì)受到另一個(gè)更新者對(duì)該行更新操作的影響,因?yàn)?,它們之間的競(jìng)爭(zhēng)關(guān)系是可以忽略不計(jì)的。因此,對(duì)于大量客戶端在同一時(shí)間更新同一行的操作,可以嘗試將更新操作合并,批量執(zhí)行。
一次修改一行中的多個(gè)列同樣受到上述原子性的保障。另外,創(chuàng)建HTable的實(shí)例并不是沒有開銷的,任何一次實(shí)例化的操作都意味著對(duì).META表的一次掃描,以確認(rèn)這個(gè)表是否存在,是否可操作(enable)。因此,我們建議每個(gè)線程僅創(chuàng)建一個(gè)HTable實(shí)例,在整個(gè)Application的生命周期內(nèi)進(jìn)行復(fù)用。
當(dāng)你考慮擁有多個(gè)HTable實(shí)例時(shí),不妨使用類HTablePool(可查看HTablePool一節(jié))靈活便捷的復(fù)用HTable實(shí)例。
小結(jié)
1.  通常在程序啟動(dòng)時(shí)創(chuàng)建HTable對(duì)象,且僅創(chuàng)建一次。
2.  每個(gè)線程持有自己的HTable實(shí)例,或者使用HTablePool。
3.  對(duì)于行的更新屬于原子操作。
CRUD操作
CRUD是一組數(shù)據(jù)庫(kù)操作的集合,即“Create,Read,Update,Delete”。HBase在HTable類中同樣提供了這些操作,后文將介紹到它們。
以下的大部分函數(shù)看起來都是通俗易懂的,但是詳細(xì)的說明看起來很相近。這意味著在對(duì)它們介紹的模式將相同,這一點(diǎn),后文將不再說明。
示例中將列出部分源代碼,完整的源碼可以在GitHub上下載,地址為https://github.com/larsgeorge/hbase-book,在“build the examples”一節(jié),將介紹如何編譯這些代碼。如果對(duì)列出的部分代碼有疑問,可以下載查閱完整的代碼。
Put操作
這組方法可以分為兩類:插入一行和插入多行,后者略復(fù)雜一些。我們將分別介紹這兩組接口。
單行插入
毫無疑問,您最想了解的接口肯定是如何將數(shù)據(jù)存放到HBase數(shù)據(jù)中,下面的接口將讓你實(shí)現(xiàn)它:
void put(Put put)
throws IOExeption
Put對(duì)象通過以下創(chuàng)造函數(shù)進(jìn)行構(gòu)造:
Put(byte[] row)
Put(byte[] row,
RowLock rowLock)
Put(byte[] row,
long ts)
Put(byte[] row,
long ts, RowLock rowLock)
您需要提供row來構(gòu)造一個(gè)Put對(duì)象,HBase中的一個(gè)row通過唯一的key和它的多個(gè)值組成。它們都以字節(jié)數(shù)組(byte[])的方式存儲(chǔ),因此,您可以將任意類型的對(duì)象當(dāng)作key。第9章介紹了如何設(shè)計(jì)rowkey,現(xiàn)在,我們假定key可以為任意的對(duì)象,它們代表了真實(shí)世界中的某個(gè)個(gè)體,比如username和order ID,它們可以是簡(jiǎn)單的數(shù)字,也可以是UUID字符串等等。
HBase非常友好地提供了工具類,用來將Java的各種類型轉(zhuǎn)化為byte[],示例3-1給出Bytes類的一轉(zhuǎn)函數(shù),這些函數(shù)能夠方便的將Java類型轉(zhuǎn)化為字節(jié)數(shù)組。
示例3-1 Bytes類的轉(zhuǎn)換函數(shù)列舉
當(dāng)你創(chuàng)建了Put對(duì)象,便可以向其中插入數(shù)據(jù)??梢酝ㄟ^使用如下的方法添加數(shù)據(jù):
通過調(diào)用一次add方法,可以加入一列數(shù)據(jù),也可以加入一個(gè)指定的時(shí)間戳。如果沒有顯示地指定時(shí)間戳,add方法會(huì)使用ts默認(rèn)的構(gòu)造函數(shù),直到Region Server將時(shí)間戳的值填入。
高級(jí)使用者在學(xué)會(huì)獲取、創(chuàng)建內(nèi)部的Cell對(duì)象后,可以調(diào)用接收KeyValue參數(shù)的add函數(shù)。一個(gè)Cell對(duì)象,代表了一個(gè)單獨(dú)、唯一的單元,類似于帶有坐標(biāo)軸的系統(tǒng),您可以使用rowkey,column family,
column qualifier和timestamp唯一的確定一個(gè)三維坐標(biāo)軸系統(tǒng)中的點(diǎn)。
使用與add方法對(duì)應(yīng)的方法get,可以從put對(duì)象中獲取到KeyValue結(jié)構(gòu)。
上述的兩個(gè)方法,第一個(gè)可以根據(jù)family和qualifier取出對(duì)應(yīng)的KeyValue值,getFamilyMap方法可以取出Put對(duì)象中的所有的column family信息。
每個(gè)KeyValue結(jié)構(gòu)都擁有一份包整的信息,包括:rowkey,column
family,qualifier,timestamp和具體的數(shù)據(jù)。它是HBase中最小的存儲(chǔ)結(jié)構(gòu)。在后文文介紹存儲(chǔ)的一節(jié)中將具體介紹。如果要詳細(xì)了解KeyValue類,請(qǐng)查看后面關(guān)于KeyValue類的具體介紹。
除了遍歷List<KeyValue>結(jié)構(gòu)查詢具體的cell,您還可以調(diào)用如下的方法:
通過限定條件,查詢是否存在相應(yīng)的Cell對(duì)象。
表3-1列出了Put類中提供的方法,要注意的是,所有的get方法只能取出Put實(shí)例中設(shè)置的Cell,因此,他們很少被使用。比如,您在一個(gè)私有方法中為Put對(duì)象設(shè)置了一些值,而在另外的地方想取出這些值。
表3-1 Put類方法一覽
Method
Description
getRow()
Returns the row key as specified when creating the Put instance.
getLockId()
Returns the optional lock ID handed into the constructor using
the rowLock parameter. Will be -1L if not set.
setWriteToWAL()
Allows you to disable the default functionality of writing the
data to the server-side write-ahead log.
getWriteToWAL()
Indicates if the data will be written to the write-ahead log.
getTimeStamp()
Retrieves the associated timestamp of the Put instance. Can be
optionally set using the constructor’s ts parameter. If not set, may return
Long.MAX_VALUE.
heapSize()
Computes the heap space required for the current Put instance.
This includes all contained data and space needed for internal structures.
isEmpty()
Checks if the family map contains any KeyValue instances.
numFamilies()
Convenience method to retrieve the size of the family map,
containing all KeyValue instances.
size()
Returns the number of KeyValue instances that will be added with
this Put.
示例3-2給出了如何使用put和get方法。
示例 3-2 向HBase數(shù)據(jù)庫(kù)中插入數(shù)據(jù)
上述代碼首先創(chuàng)建一個(gè)配置對(duì)象,然后初始化一個(gè)新的HTable對(duì)象,創(chuàng)建Put實(shí)例,插入一個(gè)名為colfam1:qual1的列,其值為val1,再插入一個(gè)colfam2:qual2的例,其值為val2,最后將這個(gè)值插入到HBase的表中。
對(duì)上述代碼示例,我們進(jìn)行了逐行詳細(xì)的介紹,后面的示例將更多地關(guān)注重點(diǎn)行進(jìn)行解釋。
您也可以使用命令行的方式來確定插入操作是否生效:
hbase(main):001:0> list
TABLE
testtable
1 row(s) in 0.0400 seconds
hbase(main):002:0> scan
‘testtable’
ROW COLUMN+CELL
row1 column=colfam1:qual1,
timestamp=1294065304642, value=val1
1
row(s) in 0.2050 seconds
timestamp(時(shí)間戳)的存在保證了HBase中的值可以有不同的版本。
當(dāng)您不指定timestamp,HBase將會(huì)把Region Server上的當(dāng)前時(shí)間插入到對(duì)應(yīng)的行上。
Put類除了默認(rèn)的構(gòu)造函數(shù),還可以帶有一個(gè)可選的參數(shù)rowLock。在”Row Lock”一節(jié)將會(huì)講到它,它使你可以在外部對(duì)一個(gè)行加鎖(譯者注:在最新的HBase版本中,該類已經(jīng)過期!)。通過使用該對(duì)象,您可以對(duì)行加鎖,訪止其它客戶端對(duì)象的訪問,從而達(dá)到對(duì)行獨(dú)占的效果。
KeyValue類
您的代碼可能會(huì)直接訪問到KeyValue對(duì)象,正如前文中提到的,KeyValue定義了坐標(biāo)系(由rowkey,column
family,column qualifier和timestamp組成)中的一個(gè)確定的點(diǎn),這個(gè)類提供了很多的創(chuàng)造函數(shù)形式,下面給出一種參數(shù)比較全面的構(gòu)造函數(shù)形式。
值得注意的是,KeyValue類以及和它相關(guān)的比較方法更多的用在HBase內(nèi)部。很少有客戶端API直接訪問原生的字節(jié)數(shù)據(jù),它們常被用來做字節(jié)層面的比較等操作。
所有的數(shù)據(jù)以及坐標(biāo)軸信息都采用了byte數(shù)據(jù)的形式存儲(chǔ),這種低層面的存儲(chǔ)方式也保證了最高的存儲(chǔ)效率,將內(nèi)部的數(shù)據(jù)結(jié)構(gòu)壓縮到最小。由于是字節(jié)數(shù)組,KeyValue構(gòu)造函數(shù)中的參數(shù)很多都由byte[]和偏移量offset組成。這種形式允許你直接以字節(jié)層面?zhèn)鬟f已經(jīng)存在的字節(jié)數(shù)組。
KeyValue中所有成員對(duì)象,都擁有g(shù)et方法獲取對(duì)應(yīng)的字節(jié)數(shù)組,包括偏移量offset和長(zhǎng)度length。當(dāng)然也可以直接返回對(duì)應(yīng)的字節(jié)數(shù)據(jù):
通過KeyValue對(duì)象的接口,可以取出內(nèi)部存儲(chǔ)的全部字節(jié)數(shù)據(jù),但很少有應(yīng)用需要這樣做,當(dāng)然也不能排除您有這樣的需求。
有兩個(gè)非常有趣的函數(shù):
byte []
getRow()
byte []
getKey()
常常有人會(huì)感到疑問,row和key有什么區(qū)別呢?在后文的“存儲(chǔ)”一節(jié),您將找到更詳細(xì)的答案。在這里,您只需要記住,row是row key的另一種叫法,也就是Put構(gòu)造函數(shù)中接受的參數(shù)row; 而key是前面介紹的可以唯一確定一個(gè)Cell的坐標(biāo)軸位置,將它用原生的byte字節(jié)表示,便是key。因此,您會(huì)很少調(diào)用到getKey()函數(shù)。更多的時(shí)候,您更習(xí)慣于使用getRow()來取得row的值。
KeyValue類也提供一組用來做比較操作的內(nèi)部類。您也可以在自己代碼中使用這些類,比如您使用客戶端API從HBase中取出了一組KeyValue對(duì)象,要對(duì)它們進(jìn)行比較、排序等處理時(shí),您便可以使用它們。
表3-2 KeyValue類中提供的比較器
Method
Description
KeyComparator
Compares two KeyValue keys,
i.e., what is returned by the getKey() method, in their raw, byte array format.
KVComparator
Wraps the raw KeyComparator,
providing the same functionality based on two given Key Value instances.
RowComparator
Compares the row key (returned
by getRow()) of two KeyValue instances.
MetaKeyComparator
Compares two keys of .META.
entries in their raw, byte array format.
MetaComparator
Special version of the
KVComparator class for the entries in the .META. catalog table. Wrapsthe
MetaKeyComparator.
RootKeyComparator
Compares two keys of -ROOT-
entries in their raw, byte array format.
RootComparator
Special version of the
KVComparator class for the entries in the -ROOT- catalog table. Wraps the RootKeyComparator.
KeyValue類以靜態(tài)對(duì)象的方法向外提供這些比較器。例如,一個(gè)public成員變量KEY_COMPARATOR,提供了KeyComparator比較器的實(shí)例。COMPARATOR成員變量實(shí)例化了應(yīng)用很廣的KVComparator類。您不必自己再為這些比較器單獨(dú)創(chuàng)建實(shí)例,可以直接使用KeyValue對(duì)象提供的這些靜態(tài)實(shí)例。比較,您要?jiǎng)?chuàng)建一個(gè)保存KeyValue對(duì)象的集合,同時(shí)保證集合中KeyValue的排列順序與HBase內(nèi)部一致,您可以使用如下的代碼:
TreeSet<KeyValue>  set  =  new  TreeSet<KeyValue>(KeyValue.COMPARATOR)
KeyValue實(shí)例中存儲(chǔ)了一個(gè)附加屬性type,來標(biāo)志一個(gè)Cell的狀態(tài)。表3-3列舉出了不同的type值。
表3-3 KeyValue實(shí)例中type屬性的取值
Type
Description
Put
The KeyValue instance
represents a normal Put operation.
Delete
This instance of KeyValue
represents a Delete operation, also known as a tombstone marker.
DeleteColumn
This is the same as Delete,
but more broadly deletes an entire column.
DeleteFamily
This is the same as Delete,
but more broadly deletes an entire column family, including all contained
columns.
您可以使用KeyValue實(shí)例的toString()方法打印出它的值,來查看它的type取值。打印結(jié)果以如下的格式輸出:
<row-key>/<family>:<qualifier>/<version>/<type>/<value-length>
本書中的一些示例代碼會(huì)使用這個(gè)方法來查看取出的數(shù)據(jù)的元數(shù)據(jù)信息。
KeyValue類中還提供了其它一些非常便捷的方法,比如:用來比較數(shù)據(jù)的部分字段,判斷它的type取值,取出它的heap大小, clone和copy信息等。KeyValue類也有一些可以創(chuàng)建比較器實(shí)例的靜態(tài)方法。您可以查詢HBase的Java Doc獲得更詳細(xì)的介紹,也可以閱讀本書的“存儲(chǔ)”一節(jié)了解更詳細(xì)的原生二進(jìn)制存儲(chǔ)格式。
客戶端寫緩存
每個(gè)Put操作都從客戶端到服務(wù)器端傳遞數(shù)據(jù)的一個(gè)RPC操作。這對(duì)于少量的操作是沒有問題的,如果一個(gè)應(yīng)用程序每秒要向HBase數(shù)據(jù)庫(kù)中插入成千上萬條記錄,這樣調(diào)用顯然是不行的。
RPC的往返調(diào)用時(shí)間是指客戶端發(fā)送請(qǐng)求給服務(wù)器,再收到服務(wù)器應(yīng)答的過程中,在網(wǎng)絡(luò)上消耗的時(shí)間,并不包括真正的數(shù)據(jù)在網(wǎng)絡(luò)上傳輸?shù)臅r(shí)間。簡(jiǎn)單地講,就是傳輸數(shù)據(jù)的額外開銷。一般情況下,在一個(gè)局域網(wǎng)中,RPC調(diào)用一次需要1ms,這意味著在一秒內(nèi),即使什么操作也不做,RPC也只能進(jìn)行1000次。
另一個(gè)重要的因素就是消息的大小:如果傳輸?shù)臄?shù)據(jù)很大時(shí),您花在RPC調(diào)用過程中的時(shí)間比例就比較低,因?yàn)榇蟛糠謺r(shí)間都在傳輸數(shù)據(jù)。以計(jì)數(shù)器為例,消息的長(zhǎng)度都很小,若一次把若干個(gè)消息合并起來更新,您會(huì)發(fā)現(xiàn)性能可以提高不少。
HBase API在客戶端內(nèi)建了一個(gè)緩存區(qū),它將若干個(gè)Put操作緩存在一起,通過一次RPC調(diào)用發(fā)給服務(wù)器端??梢酝ㄟ^如下代碼打開個(gè)屬性:
void
setAutoFlush(boolean autoFlush)
boolean
isAutoFlush()
在默認(rèn)設(shè)置下,客戶端緩存是不啟用的,您可以通過將autoFlush設(shè)置為false,從而啟用緩存策略。
table.setAutoFlush(false)
可以通過isAutuFlush判斷該策略是否生效。一旦客戶端緩存生效,你在單條Put時(shí),就不用擔(dān)心每次都會(huì)產(chǎn)生RPC操作,因?yàn)镻ut操作被緩存在了客戶端一側(cè)的緩存中。當(dāng)您想到數(shù)據(jù)寫入到HBase數(shù)據(jù)庫(kù)中時(shí),您可以調(diào)用另一個(gè)API:
void
flushCommits() throws IOException
這個(gè)方法將所有的修改操作發(fā)送給服務(wù)器,緩存區(qū)的Put操作可以覆蓋到很多的戶??蛻舳藭?huì)非常智能的將他們進(jìn)行分類并發(fā)送到多臺(tái)Region Server上去,對(duì)外看來,跟調(diào)用單次的Put操作,沒有什么區(qū)別。您并不需要擔(dān)心數(shù)據(jù)數(shù)據(jù)存放在哪里,客戶端緩存區(qū)對(duì)外部調(diào)用者來說是透明的。圖3-1給出了客戶端如何將緩區(qū)的Put操作進(jìn)行分類,并對(duì)每個(gè)Region Server進(jìn)行一次單獨(dú)的RPC操作。
圖3-1 客戶端將緩區(qū)的Put操作分類發(fā)送到不同的RegionServer上
您可以強(qiáng)制調(diào)用flush操作,但事實(shí)上這是沒有必要的??蛻舳藭?huì)記錄現(xiàn)在緩區(qū)存中的數(shù)據(jù)所占的堆棧大小,包括緩區(qū)中存儲(chǔ)數(shù)據(jù)的額外開銷,比如一些HBase內(nèi)部用到的數(shù)據(jù)結(jié)構(gòu)。當(dāng)這個(gè)大小超過了一個(gè)閥值時(shí),客戶端會(huì)隱式地發(fā)送flush命令。您可以通過如下的命令獲取和設(shè)置客戶端緩存區(qū)的大?。?div style="height:15px;">
long
getWriteBufferSize()
void
setWriteBufferSize(long writeBufferSize) throws IOException
系統(tǒng)默認(rèn)的緩存區(qū)大約為2MB(2,097,152
bytes),如果您存放的數(shù)據(jù)所占的空間很大時(shí),可以通過設(shè)置緩存區(qū)大小來保證一次RPC調(diào)用所攜帶的數(shù)據(jù)總數(shù)。
您也可以在配置文件添加如下的配置項(xiàng)來設(shè)置客戶端緩存的大?。?div style="height:15px;">
<property>
<name>hbase.client.write.buffer</name>
<value>20971520</value>
</property>
上述配置會(huì)將客戶端緩存區(qū)的大小設(shè)置為20MB。
客戶端緩區(qū)只會(huì)在如下的兩個(gè)形式下進(jìn)行flush操作:
(1)   顯式flush
通過調(diào)用flushCommits()函數(shù),將數(shù)據(jù)發(fā)送到服務(wù)器上進(jìn)行持久化。
(2)   隱式flush
當(dāng)您調(diào)用put()接口或者setWriteBufferSize()接口時(shí),這些接口會(huì)比較當(dāng)前使用的緩存大小與配置的閥值之間的關(guān)系,從而觸發(fā)flushCommit()調(diào)用,在這時(shí),客戶端的緩存區(qū)會(huì)被加鎖。若setAutoFlush為true時(shí),客戶端會(huì)將每次Put操作發(fā)送到服務(wù)器端。
示例3-3給出了API是如何操作客戶端緩存區(qū)的:
示例3-3 使用客戶端緩存
HTable table = new
HTable(conf, “testtable”);
System.out.println(“Auto
flush: ” + table.isAutoFlush());
table.setAutoFlush(false);
Put put1 = new
Put(Bytes.toBytes(“row1″));
put1.add(Bytes.toBytes(“colfam1″),
Bytes.toBytes(“qual1″),
Bytes.toBytes(“val1″));
table.put(put1);
Put put2 = new
Put(Bytes.toBytes(“row2″));
put2.add(Bytes.toBytes(“colfam1″),
Bytes.toBytes(“qual1″),
Bytes.toBytes(“val2″));
table.put(put2);
Put put3 = new
Put(Bytes.toBytes(“row3″));
put3.add(Bytes.toBytes(“colfam1″),
Bytes.toBytes(“qual1″),
Bytes.toBytes(“val3″));
table.put(put3);
Get get = new
Get(Bytes.toBytes(“row1″));
Result res1 =
table.get(get);
System.out.println(“Result:
” + res1);
table.flushCommits();
Result res2 =
table.get(get);
System.out.println(“Result:
” + res2);
上述代碼首先從獲取flush標(biāo)記的值;將會(huì)打印出“Auto flush:
true”。然后顯式的設(shè)置auto flush標(biāo)記為true,從而啟動(dòng)客戶端緩存區(qū)。隨著將一些數(shù)據(jù)插入到HBase中。接著試圖從服務(wù)器中查詢剛才插入的值,由于此時(shí),這些數(shù)據(jù)還在客戶端緩區(qū)中,因此將會(huì)打印“Result:
keyvalues=NONE”。在進(jìn)行一次顯示的flush操作后,數(shù)據(jù)被持久到服務(wù)器一側(cè),再進(jìn)行查詢,數(shù)據(jù)便可以被查詢出來了。
示例中對(duì)緩區(qū)的數(shù)據(jù)查詢的結(jié)果,您也可能出乎意料,它的輸出如下:
Auto flush: true
Result:
keyvalues=NONE
Result:
keyvalues={row1/colfam1:qual1/1300267114099/Put/vlen=4}
現(xiàn)在您還不了解get接口,但您仍可以猜想出它的作用:從服務(wù)器上讀取所需的數(shù)據(jù)。在示例中,第一次調(diào)用get操作,返回了NONE,說明了在flush發(fā)生之前,所有的數(shù)據(jù)存放在客戶端緩存中,因此get操作無法取出這些數(shù)據(jù)。
如果您非要取出客戶端緩存區(qū)中的數(shù)據(jù),您可以調(diào)用ArrayList<Put>
getWriteBuffer()操作,將您通過table.put(put)方法插入到緩存區(qū)中的Put實(shí)例取出來。
前面提到了HTable對(duì)于多線程調(diào)用是不安全的,因此對(duì)于直接訪問HTable時(shí)要格外小心,因此您越過了堆大小檢查,當(dāng)您正在修改數(shù)據(jù)時(shí),可能正在發(fā)生flush操作。
客戶端緩存是本地進(jìn)程內(nèi)存中一個(gè)簡(jiǎn)單的列表,因此,當(dāng)您關(guān)閉客戶端進(jìn)程時(shí),一定要格外小心,保證客戶端緩存中的數(shù)據(jù)已經(jīng)被flush到服務(wù)器上,如果強(qiáng)行停止程序,這些緩存中的數(shù)據(jù)將會(huì)丟失,并且是無法恢復(fù)的。
再回到RPC的往返調(diào)用時(shí)間,如果您的Cell的大小都很大,那么您的客戶端緩存的意義便不大,因?yàn)閭鬏敂?shù)據(jù)的耗時(shí)已經(jīng)占了整時(shí)耗時(shí)的大部分,在這種情形下,建議不必增加客戶端緩存的大小。
一組Put對(duì)象
客戶端API有能力添加一個(gè)單獨(dú)的Put實(shí)例,也可以一次批量的添加一組Put操作??梢酝ㄟ^以下的API實(shí)現(xiàn):
示例3-4 使用List<Put>插入數(shù)據(jù)
List<Put>
puts = new ArrayList<Put>();
Put put1 = new
Put(Bytes.toBytes(“row1″));
put1.add(Bytes.toBytes(“colfam1″),
Bytes.toBytes(“qual1″),
Bytes.toBytes(“val1″));
puts.add(put1);
Put put2 = new
Put(Bytes.toBytes(“row2″));
put2.add(Bytes.toBytes(“colfam1″),
Bytes.toBytes(“qual1″),
Bytes.toBytes(“val2″));
puts.add(put2);
Put put3 = new
Put(Bytes.toBytes(“row2″));
put3.add(Bytes.toBytes(“colfam1″),
Bytes.toBytes(“qual2″),
Bytes.toBytes(“val3″));
puts.add(put3);
table.put(puts);
首先創(chuàng)建了一個(gè)Put對(duì)象的數(shù)組,將Put對(duì)象添加到隊(duì)列中,最后將這個(gè)List寫入到HBase數(shù)據(jù)庫(kù)中。
可以使用shell命令簡(jiǎn)單測(cè)試HBase中插入的數(shù)據(jù)是否為所期望的數(shù)據(jù)。要可以注意到示例一共插入了三個(gè)column,但只有兩列,在row2中存在兩個(gè)qualifier qul1和qul2。
hbase(main):001:0>
scan ‘testtable’
ROW COLUMN+CELL
row1
column=colfam1:qual1, timestamp=1300108258094, value=val1
row2
column=colfam1:qual1, timestamp=1300108258094, value=val2
row2
column=colfam1:qual2, timestamp=1300108258098, value=val3
2 row(s) in 0.1590
seconds
Server端收到一組Put對(duì)象后,遍歷并處理這些記錄,失敗的記錄以RetriesExhaustedWithDetailsException返回到客戶端,通過該對(duì)象,客戶端可以得到哪些記錄出現(xiàn)了哪些具體錯(cuò)誤,以及重試的次數(shù)。對(duì)于不存在的column family,重試次數(shù)被設(shè)置為1,這類錯(cuò)誤是HBase無法恢復(fù)的。
那些flush操作中失敗的記錄繼續(xù)緩存在客戶端緩存中,在下一次flush操作時(shí),進(jìn)行重試。您可以通過HTable對(duì)象的getWriteBuffer()方法訪問它們。
客戶端一側(cè)也會(huì)進(jìn)行一些check操作,比如,查看一個(gè)Put實(shí)例中是否存在Column、Put實(shí)例中的所有項(xiàng)是否都為空。在這種情況下,客戶端會(huì)拋出異常強(qiáng)迫應(yīng)用程序?qū)蛻舳司彺嬷谐鲥e(cuò)的條目進(jìn)行處理。
在示例3-6中,您可以catch到異常,并且手動(dòng)的將客戶端緩區(qū)flush到服務(wù)器上去。
示例3-6 向HBase插入空Put實(shí)例
Put put1 = new Put(Bytes.toBytes(“row1″));
put1.add(Bytes.toBytes(“colfam1″),
Bytes.toBytes(“qual1″),Bytes.toBytes(“val1″));
puts.add(put1);
Put put2 = new
Put(Bytes.toBytes(“row2″));
put2.add(Bytes.toBytes(“BOGUS”),
Bytes.toBytes(“qual1″),Bytes.toBytes(“val2″));
puts.add(put2);
Put put3 = new
Put(Bytes.toBytes(“row2″));
put3.add(Bytes.toBytes(“colfam1″),
Bytes.toBytes(“qual2″),Bytes.toBytes(“val3″));
puts.add(put3);
Put put4 = new
Put(Bytes.toBytes(“row2″));
puts.add(put4);
try {
table.put(puts);
} catch (Exception
e) {
System.err.println(“Error:
” + e);
table.flushCommits();
}
首先創(chuàng)建一個(gè)內(nèi)容為空的Put對(duì)象,再創(chuàng)建一個(gè)不存在的列BOGUS, 調(diào)用put操作時(shí),會(huì)得到一個(gè)本地異常,手工進(jìn)行flush操作。
上述代碼會(huì)打印出兩個(gè)錯(cuò)誤,類似于下面:
Error:
java.lang.IllegalArgumentException: No columns to insert
Exception in
thread “main”
org.apache.hadoop.hbase.client.RetriesExhaustedWithDetailsException:
Failed 1 action:
NoSuchColumnFamilyException: 1 time,
servers with
issues: 10.0.0.57:51640,
第一條錯(cuò)誤是客戶端客戶,第二條錯(cuò)誤是在調(diào)用table.flushCommits()由服務(wù)器打印出來的。
當(dāng)您開啟了客戶端緩區(qū)之后,一些錯(cuò)誤并不能在put時(shí)立即拋出來,而是要等到發(fā)生flush時(shí)才能出現(xiàn)。
還有一點(diǎn)值得注意的是:使用List<Put>方法時(shí),您不能控制它們?cè)诜?wù)器側(cè)的順序,包括每個(gè)Put被發(fā)送到哪個(gè)服務(wù)器同樣也不受您的控制。當(dāng)您非常在意這些Put對(duì)象被發(fā)送到Server端的序列時(shí),您不得不使用更小的List,通過控制flush操作來保證順序。
原子性的compare-and-set
這是Put操作的一個(gè)變換形式,具有兩個(gè)階段:check和put。這個(gè)方法的形式如下:
boolean
checkAndPut(byte[] row, byte[] family, byte[] qualifier,
byte[] value, Put
put) throws IOException
這個(gè)調(diào)用提供原子性的、服務(wù)器側(cè)更新,同時(shí)他們帶有check操作。如果check通過,那么put操作才被執(zhí)行。如果check不通過,則put操作不會(huì)執(zhí)行。它可以被用來更新目前可能相關(guān)的值。
這個(gè)調(diào)用常被用在系統(tǒng)處理數(shù)量平衡、狀態(tài)轉(zhuǎn)換、數(shù)據(jù)處理中。最基本的原則是在某個(gè)時(shí)間點(diǎn)上讀取數(shù)據(jù)并處理后,一旦您想要把處理的結(jié)果寫回到系統(tǒng)中時(shí),您想要保證沒有別的客戶端在做同樣的事情。您使用原子性的檢查來比較value沒有被更改,然后被您的修改提交到服務(wù)器端。由于這個(gè)操作是原子性的,因此,在這個(gè)過程中不會(huì)再有別的客戶端修改這個(gè)值。
一種checkAndPut操作常見的應(yīng)用是檢查要更新的值是否存在,如果不存在就寫入,此時(shí)可以將value參數(shù)置為null。此時(shí),該API僅會(huì)在字段不存在的時(shí)候,將修改更新到數(shù)據(jù)庫(kù)中。
這個(gè)調(diào)用返回boolean類型,表示更新操作是否被執(zhí)行。示例3-7給出了checkAndPut操作的一個(gè)示例。
示例3-7 compare和set操作
Put put1 = new Put(Bytes.toBytes(“row1″));
put1.add(Bytes.toBytes(“colfam1″),
Bytes.toBytes(“qual1″),
Bytes.toBytes(“val1″));
boolean res1 =
table.checkAndPut(Bytes.toBytes(“row1″),
Bytes.toBytes(“colfam1″),
Bytes.toBytes(“qual1″), null, put1);
System.out.println(“Put
applied: ” + res1);
boolean res2 =
table.checkAndPut(Bytes.toBytes(“row1″),
Bytes.toBytes(“colfam1″),
Bytes.toBytes(“qual1″), null, put1);
System.out.println(“Put
applied: ” + res2);
Put put2 = new
Put(Bytes.toBytes(“row1″));
put2.add(Bytes.toBytes(“colfam1″),
Bytes.toBytes(“qual2″),
Bytes.toBytes(“val2″));
boolean res3 =
table.checkAndPut(Bytes.toBytes(“row1″),
Bytes.toBytes(“colfam1″),
Bytes.toBytes(“qual1″),
Bytes.toBytes(“val1″),
put2);
System.out.println(“Put
applied: ” + res3);
Put put3 = new
Put(Bytes.toBytes(“row2″));
put3.add(Bytes.toBytes(“colfam1″),
Bytes.toBytes(“qual1″),
Bytes.toBytes(“val3″));
boolean res4 =
table.checkAndPut(Bytes.toBytes(“row1″),
Bytes.toBytes(“colfam1″),
Bytes.toBytes(“qual1″),
Bytes.toBytes(“val1″),
put3);
System.out.println(“Put
applied: ” + res4);
上述代碼首先創(chuàng)建一個(gè)Put對(duì)象,檢查row1、colfam1、qual1是否存在,不存在則插入數(shù)據(jù),然后再嘗試一次。然后創(chuàng)建一個(gè)新的Put對(duì)象,使用一個(gè)完全不同的qualifier,然后再調(diào)用checkAndPut接口,只有當(dāng)前的數(shù)據(jù)存在時(shí),才會(huì)插入數(shù)的數(shù)據(jù)。最后再創(chuàng)建一個(gè)Put對(duì)象,指向一個(gè)新的row,調(diào)用checkAndPut檢查的row和插入的row不一致會(huì)得到一個(gè)異常:
Exception in
thread “main” org.apache.hadoop.hbase.DoNotRetryIOException:
Action’s getRow
must match the passed row
警告:這種Compare-and-set(CAS)調(diào)用用來檢查和插入的數(shù)據(jù)必須是同一行(同一個(gè)row),否則將會(huì)得到一個(gè)異常。
CAS調(diào)用是非常有用的,特別是在分布式系統(tǒng)中,存著很多無關(guān)聯(lián)的客戶端進(jìn)程。通過這些調(diào)用,HBase便和那些無法在多個(gè)獨(dú)立客戶端下提供一致性更新的分布式系統(tǒng)區(qū)分開來。
下一部分:HBase權(quán)威指南(中文版)——第三章(第二部分)
本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
HBase原理的簡(jiǎn)單理解
Hbase多版本的讀寫(Shell&Java API版)
hbase的過濾 Filter 之SingleColumnValueFilter
Hbase之批量數(shù)據(jù)寫入
Spark Streaming實(shí)時(shí)寫入數(shù)據(jù)到HBase
HBase Filter 過濾器之 DependentColumnFilter 詳解
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服