sqoop导入数据到hbase,在phoenix创建视图和索引流程(一)

使用sqoop向hbase导入数据的时候,一般不要让sqoop自动创建表,那样不能控制表的属性信息,比如分区等。在导入数据之前,手动在hbase中创建相应的表。
建表注意点:
1.观察源表,查看主键的长度,如果主键的字符数量比每个列的值的字符数量都要多很多,那么可以使用数据块编码,设置DATA_BLOCK_ENCODING=>'FAST_DIFF',可以减少rowkey存储所使用的空间,如果有某一列或者几列的值的字符数量特别多,远超主键字符数量,可以设置使用数据压缩,COMPRESSION=>'SNAPPY'
2.设置表的分区方式,查看源表主键的生成方式,如果是数值自增型,可以在主键的前面增加UUID,如果是16进制字符串,但不是随机值,可以考虑在主键的前面增加随机数,随机数的取值范围根据需要设置的分区的数量确定,如果是前面增加UUID,可以参考以下的建表语句:

   create 'TBL_ROAD',{NAME=>'F', DATA_BLOCK_ENCODING=>'FAST_DIFF'或者COMPRESSION=>'snappy'},{NUMREGIONS => 估算的region的数量, SPLITALGO => 'HexStringSplit'}(如果预期数据总量小于100W,不需要使用预分区)

预分区注意点:
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
大意是:在创建表时,管理员可以根据每个RegionServer上region目标数量预先拆分表,以避免在表开始填满时进行代价高昂的动态分割。此外,它还确保预分割表中的region分布在多个主机上。预分割表避免了在自动拆分期间将数据重写入多个物理文件的compact消耗。如果一个表预期增长非常快时,管理员应该为每个RegionServer为该表创建至少一个region。然而,不要立即将表分割为所需region的总数。相反,选择一个低到中间的值。对于数据较多的表,不要为表在单个regionServer创建多于一个region,特别是当您不确定表会增长到多大时。对于一个永远不会超过100mb大小的表来说,创建许多region是没有用的;一个region就可以提供足够的服务。
如果是前面增加随机数,可以参考以下的建表语句:

    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的随机数,预先创建10个分区。
此外,不要创建过多的列族,将数据存储在一个列族上可以加快数据的查询速度。
创建完成之后,可以考虑加上AggregateImplementation协处理器,可以用来通过Java代码的方式查询表的总行数,不过如果是特别大的表查询速度仍然很慢,根据经验100W-200W条数据需要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("数据表过大,查询超时")
        }finally {
            if(aggregationClient!=null) aggregationClient .close()
         }
   }

数据导入命令参考:

    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

命令参数说明:
-Dmapred.child.java.opts='-Djava.security.egd=file:/dev/../dev/urandom':
如果不加上这个参数,导入数据耗时时间较长的时候可能会出现以下异常:

  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':
该参数指定了在yarn上该任务的任务名称,方便查询任务的进度信息。
当源表存在联合主键的时候,可使用以下的方式来指定rowkey,如下使用COL1,COL2作为联合主键

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的值为下划线拼接的各列的值
因为作为联合主键的任意列的值不能为null,当源表存在这样的联合主键的时候不会出现导入异常,但是如果源表不存在任何主键,但是导入到hbase需要指定rowkey,可以选择若干列或者一列作为主键,这样可能会出现重复数据覆盖的现象,指定为rowkey的任意列不能出现null,否则导入报错
数据导入完成之后,由于直接查询hbase较为复杂,可以通过phoenix进行数据查询,在phoenix中创建相应的视图或者表以映射hbase表,Apache Phoenix接受SQL查询,将其编译为一系列HBase扫描,并协调这些扫描的运行以生成常规JDBC结果集。
如果只做查询,强烈建议使用 phoenix 视图方式映射,删除视图不影响 hbase 源数据,语法如下:

    create view TBL_ROAD(PK VARCHAR PRIMARY KEY,F.NAME VARCHAR,F.LOCATION VARCHAR);

如果必须要表映射,需要禁用列映射规则(会降低查询性能),如下:

  CREATE TABLE TBL_ROAD(PK VARCHAR PRIMARY KEY,F.NAME VARCHAR,F.LOCATION VARCHAR)COLUMN_ENCODED_BYTES=0;

还有一些创建表或者视图时的可选项,官网http://phoenix.apache.org/language/index.html有详细的介绍,我就不搬砖了。
索引类型:http://phoenix.apache.org/secondary_indexing.html
索引主要有global index和local index,上面网址介绍了这两者的区别,下面主要介绍索引的创建和可能出现的问题。
1.索引创建的必要性,如果表的数量较小,无需进行索引创建,查询速度也令人满意。
2.数据导入完成之后,为了加快查询,需要加上索引,如果数据量较大,这时不能在phoenix命令行直接对表或者视图创建索引,会出现超时异常
这时需要启动mapreduce任务批量创建索引
异步创建local index:

create local index TBL_ROAD_LOCAL_INDEX_1 ON TBL_ROAD(NAME,LOCATION)BLOOMFILTER='ROW' async;

启动mapreduce任务填充索引:

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任务完成之后,会自动进行索引的激活,之后该索引可以参与相关的查询
说明:增加hbase和yarn的配置信息,保证mapreduce任务运行在yarn上,否则任务运行在本地,当hbase表数据量稍大的时候,会引起内存溢出的异常
--data-table 需要创建索引的表或者视图的名称
--index-table 创建的索引的名称
--output-path mapreduce任务创建的索引文件的临时存储位置,任务会在参数指定的目录下创建子目录,名称为需要创建索引的表的名称,任务完成之后会将临时数据移动到索引表目录下,删除创建的子目录,并将创建的索引激活以用于查询
注意点:
在向单分区表load数据或者在多分区表上创建单分区索引的时候,可能会出现以下异常:

    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)

该问题的出现属于HBase本身的限制,HBase在Bulk Load时默认一个region的hfile个数是32,当hfile文件个数超过32个时则会报上述错误
可以通过以下方式解决:
修改HBase配置,调大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的数量超过这个限制的时候,可能会引起store的写入异常,这个没有具体测试过,但是尽量避免,这种情况可以通过创建多分区索引表避免.
数据直接导入到hbase:
在hbase中建表,并进行预分区并接收导入的数据,然后在phoenix中建表进行映射,那么不能使用SALT_BUCKETS属性,否则出现主键列查询异常(实际hbase中是有数据的),之后在phoenix中创建索引辅助查询,global index索引表不是分区的,默认只是一个分区,如果创建的是local index,由于只是在hbase表中增加一个新的列族,保证索引数据和源数据在一个region上.
数据直接导入phoenix:
在phoenix中建表,指定SALT_BUCKETS,并直接接收数据,创建的global index表也是自动分区的,分区数量和源表相同,这样流程稍微复杂一些,需要分成两步进行,首先使用sqoop将数据导入到hdfs上,然后使用CsvBulkLoadTool读取hdfs文件导入到phoenix表中,如果之前已经创建了索引,索引数据同步更新,优点是可以保持原本字段的数据类型,而不需要像直接导入hbase那样,设置phoenix所有字段类型为VARCHAR.
如果命令执行出现namespaces mapping异常,关于如何配置namespace mapping,可以查看http://phoenix.apache.org/namspace_mapping.html,配置这个的主要目的是将在phoenix中的schema和hbase的namespace进行映射,不配置也没有关系,如果出现了异常,可以使用以下命令进行phoenix-4.14.0-cdh5.14.2-client.jar的更新
说明:我搭建的cdh环境为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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容

  • 为什么需要Secondary Index 对于HBase而言,如果想精确地定位到某行记录,唯一的办法是通过rowk...
    4ea0af17fd67阅读 10,905评论 0 5
  • 参考:https://www.jianshu.com/p/569106a3008f 最近在逐步跟进Hbase的相关...
    博弈史密斯阅读 839评论 1 1
  • HBase那些事 @(大数据工程学院)[HBase, Hadoop, 优化, HadoopChen, hbase]...
    分痴阅读 3,919评论 3 17
  • 1 使用概述 2 环境配置 3 SQL特性详解 1)create:可以创建一张表或视图。表名如果没有用双引号括起来...
    4ea0af17fd67阅读 12,403评论 0 10
  • 本文首先简单介绍了HBase,然后重点讲述了HBase的高并发和实时处理数据 、HBase数据模型、HBase物理...
    达微阅读 2,717评论 1 13