使用sqoop向hbase導(dǎo)入數(shù)據(jù)的時候,一般不要讓sqoop自動創(chuàng)建表十饥,那樣不能控制表的屬性信息窟勃,比如分區(qū)等。在導(dǎo)入數(shù)據(jù)之前逗堵,手動在hbase中創(chuàng)建相應(yīng)的表秉氧。
建表注意點:
1.觀察源表,查看主鍵的長度蜒秤,如果主鍵的字符數(shù)量比每個列的值的字符數(shù)量都要多很多汁咏,那么可以使用數(shù)據(jù)塊編碼亚斋,設(shè)置DATA_BLOCK_ENCODING=>'FAST_DIFF',可以減少rowkey存儲所使用的空間,如果有某一列或者幾列的值的字符數(shù)量特別多攘滩,遠超主鍵字符數(shù)量帅刊,可以設(shè)置使用數(shù)據(jù)壓縮,COMPRESSION=>'SNAPPY'
2.設(shè)置表的分區(qū)方式漂问,查看源表主鍵的生成方式赖瞒,如果是數(shù)值自增型,可以在主鍵的前面增加UUID蚤假,如果是16進制字符串冒黑,但不是隨機值,可以考慮在主鍵的前面增加隨機數(shù)勤哗,隨機數(shù)的取值范圍根據(jù)需要設(shè)置的分區(qū)的數(shù)量確定抡爹,如果是前面增加UUID,可以參考以下的建表語句:
create 'TBL_ROAD',{NAME=>'F', DATA_BLOCK_ENCODING=>'FAST_DIFF'或者COMPRESSION=>'snappy'},{NUMREGIONS => 估算的region的數(shù)量, SPLITALGO => 'HexStringSplit'}(如果預(yù)期數(shù)據(jù)總量小于100W,不需要使用預(yù)分區(qū))
預(yù)分區(qū)注意點:
Administrators can pre-split tables during table creation based on the target number of
regions per RegionServer to avoid costly dynamic splitting as the table starts to fill up. In
addition, it ensures that the regions in the pre-split table are distributed across many host
machines. Pre-splitting a table avoids the cost of compactions required to rewrite the data
into separate physical files during automatic splitting. If a table is expected to grow very
large, administrators should create at least one region per RegionServer. However, do
not immediately split the table into the total number of desired regions. Rather, choose
a low to intermediate value. For multiple tables, do not create more than one region per
RegionServer, especially if you are uncertain how large the table will grow. Creating too
many regions for a table that will never exceed 100 MB in size isn't useful; a single region
can adequately services a table of this size
大意是:在創(chuàng)建表時芒划,管理員可以根據(jù)每個RegionServer上region目標數(shù)量預(yù)先拆分表冬竟,以避免在表開始填滿時進行代價高昂的動態(tài)分割。此外民逼,它還確保預(yù)分割表中的region分布在多個主機上泵殴。預(yù)分割表避免了在自動拆分期間將數(shù)據(jù)重寫入多個物理文件的compact消耗。如果一個表預(yù)期增長非称床裕快時笑诅,管理員應(yīng)該為每個RegionServer為該表創(chuàng)建至少一個region。然而,不要立即將表分割為所需region的總數(shù)疮鲫。相反,選擇一個低到中間的值吆你。對于數(shù)據(jù)較多的表,不要為表在單個regionServer創(chuàng)建多于一個region俊犯,特別是當您不確定表會增長到多大時妇多。對于一個永遠不會超過100mb大小的表來說,創(chuàng)建許多region是沒有用的;一個region就可以提供足夠的服務(wù)燕侠。
如果是前面增加隨機數(shù)者祖,可以參考以下的建表語句:
create 'TBL_ROAD',{NAME=>'F', DATA_BLOCK_ENCODING=>'FAST_DIFF'或者COMPRESSION=>'snappy'},SPLITS=>['0|','1|','2|','3|','4|','5|','6|','7|','8|']
上面的建表語句是為源表的非隨機16進制字符串主鍵增加0-9的隨機數(shù),預(yù)先創(chuàng)建10個分區(qū)绢彤。
此外七问,不要創(chuàng)建過多的列族,將數(shù)據(jù)存儲在一個列族上可以加快數(shù)據(jù)的查詢速度茫舶。
創(chuàng)建完成之后械巡,可以考慮加上AggregateImplementation協(xié)處理器,可以用來通過Java代碼的方式查詢表的總行數(shù),不過如果是特別大的表查詢速度仍然很慢坟比,根據(jù)經(jīng)驗100W-200W條數(shù)據(jù)需要1s
參考命令如下:
disable 'TBL_ROAD'
alter 'TBL_ROAD', METHOD =>'table_att','coprocessor'=>'|org.apache.hadoop.hbase.coprocessor.AggregateImplementation||'
enable 'TBL_ROAD'
代碼參考:
def getRowNumOfSpecifiedTable(tableName: String): Long= {
import org.apache.hadoop.hbase.client.coprocessor.AggregationClient
import org.apache.hadoop.hbase.HBaseConfiguration
import org.apache.hadoop.hbase.TableName
import org.apache.hadoop.hbase.client.Scan
import org.apache.hadoop.hbase.client.coprocessor.LongColumnInterpreter
val configuration = HBaseConfiguration.create()
configuration.addResource("core-site.xml")
configuration.addResource("hbase-site.xml")
configuration.addResource("hdfs-site.xml")
val scan = new Scan()
val table = TableName.valueOf(tableName.trim)
val aggregationClient = new AggregationClient(configuration)
try {
val result = aggregationClient.rowCount(table, new LongColumnInterpreter(), scan)
result
} catch {
case _: Exception => throw new RuntimeExceprion("數(shù)據(jù)表過大芦鳍,查詢超時")
}finally {
if(aggregationClient!=null) aggregationClient .close()
}
}
數(shù)據(jù)導(dǎo)入命令參考:
sqoop import -Dmapred.child.java.opts='-Djava.security.egd=file:/dev/../dev/urandom' -Dmapred.job.name='TBL_ROAD_1' --connect jdbc:oracle:thin:@localhost:1521:orcl --username xxx --password 123456 --query 'SELECT ID, NAME, LOCATION FROM TBL_ROAD WHERE $CONDITIONS' --hbase-table TBL_ROAD --hbase-row-key ID --column-family F -m 1
命令參數(shù)說明:
-Dmapred.child.java.opts='-Djava.security.egd=file:/dev/../dev/urandom':
如果不加上這個參數(shù),導(dǎo)入數(shù)據(jù)耗時時間較長的時候可能會出現(xiàn)以下異常:
ERROR manager.SqlManager: Error executing statement: java.sql.SQLRecoverableException: IO Error: Connection reset
java.sql.SQLRecoverableException: IO Error: Connection reset
at oracle.jdbc.driver.T4CConnection.logon(T4CConnection.java:752)
at oracle.jdbc.driver.PhysicalConnection.connect(PhysicalConnection.java:662)
at oracle.jdbc.driver.T4CDriverExtension.getConnection(T4CDriverExtension.java:32)
at oracle.jdbc.driver.OracleDriver.connect(OracleDriver.java:560)
at java.sql.DriverManager.getConnection(DriverManager.java:571)
at java.sql.DriverManager.getConnection(DriverManager.java:233)
at org.apache.sqoop.manager.OracleManager.makeConnection(OracleManager.java:325)
at org.apache.sqoop.manager.GenericJdbcManager.getConnection(GenericJdbcManager.java:52)
-Dmapred.job.name='TBL_ROAD_1':
該參數(shù)指定了在yarn上該任務(wù)的任務(wù)名稱葛账,方便查詢?nèi)蝿?wù)的進度信息柠衅。
當源表存在聯(lián)合主鍵的時候,可使用以下的方式來指定rowkey,如下使用COL1,COL2作為聯(lián)合主鍵
sqoop import -Dmapred.child.java.opts='-Djava.security.egd=file:/dev/../dev/urandom' -Dmapred.job.name='TBL_ROAD_1' --connect jdbc:oracle:thin:@localhost:1521:orcl --username xxx --password 123456 --query 'SELECT ID, NAME, LOCATION,COL1,COL2,COL3 FROM TBL_ROAD WHERE $CONDITIONS' --hbase-table TBL_ROAD --hbase-row-key COL1,COL2 --column-family F -m 1
rowkey的值為下劃線拼接的各列的值
因為作為聯(lián)合主鍵的任意列的值不能為null,當源表存在這樣的聯(lián)合主鍵的時候不會出現(xiàn)導(dǎo)入異常,但是如果源表不存在任何主鍵,但是導(dǎo)入到hbase需要指定rowkey,可以選擇若干列或者一列作為主鍵,這樣可能會出現(xiàn)重復(fù)數(shù)據(jù)覆蓋的現(xiàn)象,指定為rowkey的任意列不能出現(xiàn)null,否則導(dǎo)入報錯
數(shù)據(jù)導(dǎo)入完成之后,由于直接查詢hbase較為復(fù)雜籍琳,可以通過phoenix進行數(shù)據(jù)查詢菲宴,在phoenix中創(chuàng)建相應(yīng)的視圖或者表以映射hbase表,Apache Phoenix接受SQL查詢趋急,將其編譯為一系列HBase掃描喝峦,并協(xié)調(diào)這些掃描的運行以生成常規(guī)JDBC結(jié)果集。
如果只做查詢呜达,強烈建議使用 phoenix 視圖方式映射谣蠢,刪除視圖不影響 hbase 源數(shù)據(jù),語法如下:
create view TBL_ROAD(PK VARCHAR PRIMARY KEY,F.NAME VARCHAR,F.LOCATION VARCHAR);
如果必須要表映射查近,需要禁用列映射規(guī)則(會降低查詢性能)眉踱,如下:
CREATE TABLE TBL_ROAD(PK VARCHAR PRIMARY KEY,F.NAME VARCHAR,F.LOCATION VARCHAR)COLUMN_ENCODED_BYTES=0;
還有一些創(chuàng)建表或者視圖時的可選項,官網(wǎng)http://phoenix.apache.org/language/index.html有詳細的介紹霜威,我就不搬磚了谈喳。
索引類型:http://phoenix.apache.org/secondary_indexing.html
索引主要有g(shù)lobal index和local index,上面網(wǎng)址介紹了這兩者的區(qū)別戈泼,下面主要介紹索引的創(chuàng)建和可能出現(xiàn)的問題婿禽。
1.索引創(chuàng)建的必要性,如果表的數(shù)量較小,無需進行索引創(chuàng)建,查詢速度也令人滿意大猛。
2.數(shù)據(jù)導(dǎo)入完成之后,為了加快查詢,需要加上索引,如果數(shù)據(jù)量較大扭倾,這時不能在phoenix命令行直接對表或者視圖創(chuàng)建索引,會出現(xiàn)超時異常
這時需要啟動mapreduce任務(wù)批量創(chuàng)建索引
異步創(chuàng)建local index:
create local index TBL_ROAD_LOCAL_INDEX_1 ON TBL_ROAD(NAME,LOCATION)BLOOMFILTER='ROW' async;
啟動mapreduce任務(wù)填充索引:
HADOOP_CLASSPATH=/opt/cloudera/parcels/CDH/lib/hbase/lib/hbase-protocol-1.2.0-cdh5.14.2.jar:/etc/hbase/conf:/etc/spark/conf.cloudera.spark_on_yarn/yarn-conf hadoop jar /opt/cloudera/parcels/APACHE_PHOENIX/lib/phoenix/phoenix-4.14.0-cdh5.14.2-client.jar org.apache.phoenix.mapreduce.index.IndexTool --data-table src-tablename --index-table index-tablename --output-path /indexesPath(hdfs path)
mapreduce任務(wù)完成之后,會自動進行索引的激活,之后該索引可以參與相關(guān)的查詢
說明:增加hbase和yarn的配置信息,保證mapreduce任務(wù)運行在yarn上,否則任務(wù)運行在本地,當hbase表數(shù)據(jù)量稍大的時候,會引起內(nèi)存溢出的異常
--data-table 需要創(chuàng)建索引的表或者視圖的名稱
--index-table 創(chuàng)建的索引的名稱
--output-path mapreduce任務(wù)創(chuàng)建的索引文件的臨時存儲位置,任務(wù)會在參數(shù)指定的目錄下創(chuàng)建子目錄胎署,名稱為需要創(chuàng)建索引的表的名稱吆录,任務(wù)完成之后會將臨時數(shù)據(jù)移動到索引表目錄下,刪除創(chuàng)建的子目錄琼牧,并將創(chuàng)建的索引激活以用于查詢
注意點:
在向單分區(qū)表load數(shù)據(jù)或者在多分區(qū)表上創(chuàng)建單分區(qū)索引的時候,可能會出現(xiàn)以下異常:
ERROR mapreduce.LoadIncrementalHFiles: Trying to load more than 32 hfiles to family d of region with start key
Exception in thread "main" java.io.IOException: Trying to load more than 32 hfiles to one family of one region
at org.apache.hadoop.hbase.mapreduce.LoadIncrementalHFiles.doBulkLoad(LoadIncrementalHFiles.java:288)
at org.apache.hadoop.hbase.mapreduce.LoadIncrementalHFiles.run(LoadIncrementalHFiles.java:842)
at org.apache.hadoop.util.ToolRunner.run(ToolRunner.java:70)
at org.apache.hadoop.util.ToolRunner.run(ToolRunner.java:84)
at org.apache.hadoop.hbase.mapreduce.LoadIncrementalHFiles.main(LoadIncrementalHFiles.java:847)
該問題的出現(xiàn)屬于HBase本身的限制,HBase在Bulk Load時默認一個region的hfile個數(shù)是32,當hfile文件個數(shù)超過32個時則會報上述錯誤
可以通過以下方式解決:
修改HBase配置哀卫,調(diào)大hbase.mapreduce.bulkload.max.hfiles.perRegion.perFamily值巨坊,需要重啟HBase才生效
<property>
<name>hbase.mapreduce.bulkload.max.hfiles.perRegion.perFamily</name>
<value>100</value>
</property>
但是hbase表原本存在hbase.hstore.blockingStoreFiles的限制,當移動的hfile的數(shù)量超過這個限制的時候,可能會引起store的寫入異常,這個沒有具體測試過,但是盡量避免,這種情況可以通過創(chuàng)建多分區(qū)索引表避免.
數(shù)據(jù)直接導(dǎo)入到hbase:
在hbase中建表,并進行預(yù)分區(qū)并接收導(dǎo)入的數(shù)據(jù),然后在phoenix中建表進行映射,那么不能使用SALT_BUCKETS屬性,否則出現(xiàn)主鍵列查詢異常(實際hbase中是有數(shù)據(jù)的),之后在phoenix中創(chuàng)建索引輔助查詢,global index索引表不是分區(qū)的,默認只是一個分區(qū),如果創(chuàng)建的是local index,由于只是在hbase表中增加一個新的列族,保證索引數(shù)據(jù)和源數(shù)據(jù)在一個region上.
數(shù)據(jù)直接導(dǎo)入phoenix:
在phoenix中建表,指定SALT_BUCKETS,并直接接收數(shù)據(jù),創(chuàng)建的global index表也是自動分區(qū)的,分區(qū)數(shù)量和源表相同,這樣流程稍微復(fù)雜一些,需要分成兩步進行,首先使用sqoop將數(shù)據(jù)導(dǎo)入到hdfs上,然后使用CsvBulkLoadTool讀取hdfs文件導(dǎo)入到phoenix表中,如果之前已經(jīng)創(chuàng)建了索引,索引數(shù)據(jù)同步更新,優(yōu)點是可以保持原本字段的數(shù)據(jù)類型,而不需要像直接導(dǎo)入hbase那樣,設(shè)置phoenix所有字段類型為VARCHAR.
如果命令執(zhí)行出現(xiàn)namespaces mapping異常,關(guān)于如何配置namespace mapping,可以查看http://phoenix.apache.org/namspace_mapping.html,配置這個的主要目的是將在phoenix中的schema和hbase的namespace進行映射此改,不配置也沒有關(guān)系趾撵,如果出現(xiàn)了異常,可以使用以下命令進行phoenix-4.14.0-cdh5.14.2-client.jar的更新
說明:我搭建的cdh環(huán)境為5.14.2,不要直接照搬命令,參考實際的情況操作占调。
jar uf /opt/cloudera/parcels/APACHE_PHOENIX/lib/phoenix/phoenix-4.14.0-cdh5.14.2-client.jar /etc/hbase/conf/hbase-site.xml