ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Sqoop导入HBase的两种模式:Put与BulkLoad原理、调优和踩坑实录

Sqoop导入HBase的两种模式:Put与BulkLoad原理、调优和踩坑实录 1. 项目定位为什么偏偏用Sqoop把数据灌进HBase先交代一下背景。做大数据平台的同学都懂业务系统里的数据绝大多数还是躺在MySQL、Oracle这类关系型数据库里动辄几亿条订单、用户行为日志单表查询已经明显吃力。这时候HBase天然适合干这件事分布式存储、海量数据、毫秒级随机读写再加上列族设计灵活基本就是为海量结构化数据做实时查询准备的。但问题随之而来——历史数据怎么从MySQL平滑迁移到HBase手工写Java API去读MySQL再写HBase慢不说还得自己处理分片、并发、断点续传。于是Sqoop这个老牌ETL工具被拉出来干活了。Sqoop和HBase集成其实有个很多人没意识到的坑Sqoop导入HBase有两种完全不同的模式一种是常规的Put模式走HBase客户端API逐条写入另一种是BulkLoad模式直接绕过API生成HFile再加载。两种模式在数据量、写入性能、对线上集群的影响上差异巨大选错了轻则作业跑得慢重则直接把HBase集群搞到Region Server频繁GC甚至宕机。这篇文章我不打算复述官方文档就基于我实际跑过的任务把两种模式背后的原理拆开讲清楚它们各自适用什么场景、怎么调参、踩过哪些坑最后附一份常见问题速查。如果你是刚接触HBase数据导入的工程师或者正在纠结导入方案选型这篇应该能帮你省不少时间。2. 两种思路的本质区别API写入 vs 文件落地2.1 为什么要区分“写API”和“写文件”理解这两种模式前得先搞清楚HBase的数据写入链路。正常情况下客户端往HBase写一条数据请求会先到Region Server写入WAL日志再进MemStore等MemStore满了之后刷写成HFile存储在HDFS上。这条链路保证了实时写入的可靠性和查询的一致性但代价是每一次Put都要经过网络传输、日志写盘、内存维护这一整套流程写入吞吐量再高也有天花板。BulkLoad的思路完全不一样它绕开了WAL和MemStore直接生成HBase底层存储格式的HFile文件然后把文件加载到Region里。这个过程有点像搬家时不用一件一件往新家搬家具而是让搬家公司把家具打包成集装箱一整箱一整箱运过去到了再拆箱摆放。因为少了逐条写日志、写内存的环节BulkLoad的导入速度通常比Put模式快好几倍而且对Region Server的内存、CPU冲击小得多。2.2 Sqoop到底怎么实现这两种模式Sqoop导入HBase时两种模式的触发方式其实很简单不带任何特殊参数Sqoop默认就是Put模式加上--hbase-bulkload参数就切换到BulkLoad模式。但参数背后对应的执行路径完全不同。Put模式下Sqoop会把导入过程封装成一个MapReduce作业每个Map任务从关系型数据库读取数据分片然后在Mapper的map()方法里逐条调用HBase的Put API写入目标表。这时的写入请求和普通业务写入没有本质区别每条数据都是独立走完WAL、MemStore、刷写这条链路。BulkLoad模式下Sqoop依然跑MapReduce作业但每个Mapper的输出不再是Put请求而是直接生成HFile临时文件并按照目标表的Region边界做好切分。整个作业跑完后数据以HFile文件的形式躺在HDFS上接下来再用HBase提供的LoadIncrementalHFiles工具把这些文件整体加载到对应的Region里。这个过程只涉及HDFS层面的文件移动和元数据更新不涉及逐条写入。这才是两种模式的核心分水岭一个在数据访问层逐条写入一个在存储文件层整体加载。理解了这一点后面所有的性能差异、坑点、适用场景推导就都顺理成章了。3. 实战准备环境、依赖和基础配置3.1 环境版本与集群构成我先说一下自己验证时的环境大家做参考不一定要完全一致但版本差太远的话行为和报错信息会有出入。集群是三节点的CDH发行版HBase 1.2.0Hadoop 2.6.0Sqoop 1.4.6MySQL 5.7。Sqoop装在其中一个节点上作为客户端工具使用。如果你是自己搭的原生集群需要确认几件事Sqoop版本1.4.6和1.4.7是主流HBase版本1.x和2.x在API上略有差异BulkLoad的加载命令在2.x里还能兼容Hadoop版本这决定了HFile的格式兼容性。环境差异导致的报错后面排查章节会专门说。3.2 把HBase相关jar包塞进Sqoop这是新手最容易卡住的地方。Sqoop本身只负责从关系型数据库抽数它要和HBase通信就必须能加载到HBase的客户端类库。默认情况下Sqoop的lib目录下是没有HBase相关jar的直接跑命令大概率会报ClassNotFoundException。我当时的做法是把HBase安装目录下lib目录里的jar包软链到Sqoop的lib目录下。要注意不要一股脑把所有jar都复制过去可能会和Sqoop本身的依赖版本冲突。建议先只复制这几个核心的ln -s /opt/hbase/lib/hbase-client-*.jar /opt/sqoop/lib/ ln -s /opt/hbase/lib/hbase-common-*.jar /opt/sqoop/lib/ ln -s /opt/hbase/lib/hbase-server-*.jar /opt/sqoop/lib/ ln -s /opt/hbase/lib/hbase-protocol-*.jar /opt/sqoop/lib/ ln -s /opt/hbase/lib/hbase-hadoop2-compat-*.jar /opt/sqoop/lib/ ln -s /opt/hbase/lib/hbase-hadoop-compat-*.jar /opt/sqoop/lib/ ln -s /opt/hbase/lib/htrace-core-*.jar /opt/sqoop/lib/还要注意HBase的jar里很多依赖了ZooKeeper所以ZooKeeper的jar也得出现在Sqoop的classpath里。HBase客户端连集群时默认是走ZooKeeper去拿元数据信息和Region Server地址的缺了ZooKeeper的jar同样会报连接问题。如果集群启用了Kerberos认证还需要额外加相应的认证配置这个在生产环境很常见。配置完成后可以先用简单命令验证一下Sqoop能不能正常解析HBase相关参数sqoop help | grep -i hbase能看到hbase-table、hbase-bulkload这些关键字说明类加载基本没问题。3.3 目标表设计先想好RowKey和列族不管是哪种导入模式目标HBase表都得提前设计好。Sqoop不会主动帮你做复杂的表设计你可以用--hbase-create-table让它自动建表但这样建出来的表往往很粗糙——单个列族、RowKey和列名都直接搬源头表的字段名基本没法应付真实查询场景。我习惯的做法是先手动建表再把Sqoop的自动建表关掉。建表时重点关注RowKey的设计。举例来说如果源头表是订单表主键是自增id直接把id当RowKey的话导入时所有数据会集中写入少数几个Region形成写热点Region Server压力会非常不均衡。可以用订单号加盐、反转、加时间戳前缀等方式打散。列族的设计也直接影响BulkLoad的效率。HBase官方建议一个表不超过三个列族每个列族对应不同的读写模式。Sqoop导入时--column-family参数只能指定一个列族如果源表字段要拆到多个列族就得靠后续的HBase处理或者多个Sqoop作业去完成这点要提前想清楚。列族如果设置了比较激进的压缩算法比如Snappy或LZ4BulkLoad生成的HFile会更小加载时对磁盘和网络的占用也更低。我在生产中一般都会开压缩除非查询模式对CPU开销特别敏感。4. Put模式实现详解从命令到执行链路4.1 一份可以照抄的基础命令Put模式的Sqoop命令不复杂核心就是给Sqoop指定--hbase-table和--column-family两个参数让MapReduce作业里的Mapper知道把数据写到哪个表的哪个列族。这里给一份我实际用过的模板sqoop import \ --connect jdbc:mysql://192.168.1.10:3306/business?useSSLfalsecharacterEncodingutf8 \ --username root \ --password xxxx \ --table orders \ --columns order_id,user_id,amount,status,create_time \ --hbase-table orders_hbase \ --column-family cf \ --hbase-row-key order_id \ --hbase-create-table \ --split-by order_id \ --num-mappers 8先解释各个参数的含义。--hbase-table指定目标HBase表名如果表不存在--hbase-create-table会自动创建默认建一个名为cf的列族。--hbase-row-key指定用哪一列作为HBase的RowKey这个键在源头表里必须是唯一的否则后面的数据会覆盖前面的数据。--column-family指定写入的列族Sqoop会把除RowKey外的所有列都塞进这个列族里列名就是源表的字段名。--split-by和--num-mappers要一起说。Sqoop从MySQL导数据时需要把一个查询拆成多个分片分给不同的Map任务并行执行。--split-by order_id告诉Sqoop按照order_id字段做分片Sqoop会先执行一条SELECT MIN(order_id), MAX(order_id) FROM orders然后根据最小值和最大值把整个区间切成--num-mappers个分片。如果主键不是order_id但order_id上有索引这里用order_id做split字段通常比用自增主键效果更好因为数据分布更均匀。4.2 Mapper内部到底发生了什么当MapReduce作业启动后每个Mapper会拿到一个属于自己的数据分片比如负责order_id在100000到200000之间的数据。Mapper从MySQL拉取这批数据后对每一行记录做这些事先根据--hbase-row-key指定的字段构造一个Put对象然后遍历这一行的所有其他字段调用Put.addColumn()方法加到同一个Put里最后通过HBase的Table.put()接口写入。这个过程里的性能瓶颈有几个。首先是网络往返每条Put都要从Sqoop客户端机器发到Region Server即使开启了HBase客户端的自动Flush缓冲单条写入的吞吐量依然有限。其次是WAL日志写盘每个Region Server都要把日志落盘一次保证数据不丢但这部分IO开销很大。最后是MemStore的内存压力大量Put涌进来后MemStore达到阈值会触发刷写刷写期间Region会短暂阻塞写入。所以Put模式适合的场景是实时性要求相对高、数据量不大的同步任务比如每小时同步几万条增量订单这种量级Put模式完全能扛住而且实现简单出了问题也容易排查。4.3 实测参数调优从2万行/秒到8万行/秒Put模式刚上线时我遇到过一个典型的性能问题。当时要导一张几百万行的用户表默认参数跑了一个多小时算下来每秒才2万行左右比预想的慢很多。分析下来主要有三个原因第一个是--num-mappers太小。默认值是4但我当时的目标表只有6个Region4个Mapper并发写没有充分压满Region Server的写入能力。我把--num-mappers调到12后速度立刻翻了一倍多。当然Mapper数量也不是越大越好Mapper太多会导致每个Region同时接收大量写入触发频繁刷写和Compaction反而拖慢速度。经验值是按Region数量的1.5到2倍设置Mapper数同时看一下Region Server的CPU和磁盘IO超过60%就要降并发。第二个是HBase客户端的写入缓冲没调大。Sqoop的HBase写入默认走的是HBase客户端API可以通过设置hbase.client.write.buffer来增大批量写入的缓冲阈值默认是2MB我把它调到8MB后批量写入的效果明显改善减少了RPC次数。第三个是RowKey顺序性问题。如果RowKey是单调递增的写入时会集中打在同一个Region上其他Region空闲整体吞吐当然上不去。可以在导入前对源数据做一次预处理把RowKey加盐比如取order_id % 16拼到前面这样写入就能均匀分布到16个不同的Region上。但要注意加盐后的RowKey在查询时也要带上同样的盐值前缀否则查不到数据属于典型的空间换时间的取舍。4.4 Put模式的坑脏数据、超时和覆盖Put模式踩过的坑我印象最深的是脏数据问题和单条超时问题。脏数据问题出现在源表字段类型和HBase存储类型不一致时。MySQL里的整数、浮点、日期到了HBase全部变成字节数组。Sqoop默认会做字符串转换但遇到NULL值时有时候是空字节数组有时候是字符串null这个行为在不同版本里不完全一致。查询时如果反序列化方式不匹配数据就是错的。建议导入前先小范围抽一批数据用HBase Shell看一眼实际存储内容再决定要不要加--null-string、--null-non-string这类参数来统控。单条超时问题更隐蔽。默认情况下HBase客户端RPC有超时设置一旦某条Put请求在Region Server端处理时间过长客户端会抛异常Mapper任务直接失败。而Region Server处理超时的原因基本都指向了GC。大量写入导致堆内存吃紧触发Full GCRegion Server在和ZooKeeper的心跳上卡了太久被判定为宕机这个Region的所有后续写入都会被拒绝。我在生产环境曾经因为这个问题把导入作业跑挂了三次后来在Region Server启动参数里把HRegionServer的堆内存从8G提到16G同时把新生代比例调大情况才稳定下来。这里想强调一点Put模式看起来简单但一旦数据量大到一定规模它对HBase集群写入链路的压力是实打实的不能因为它简单就忽略了对集群的保护。5. BulkLoad模式实现详解HFile生成与加载5.1 BulkLoad的整体流程拆解BulkLoad模式分两个阶段第一阶段用Sqoop生成HFile第二阶段用HBase工具加载HFile。第一阶段依然是MapReduce作业但Mapper的输出变成了HFile格式的临时文件这些文件会按照目标表的Region边界做预分区切分存放在HDFS的临时目录下。第二阶段是核心HBase的LoadIncrementalHFiles工具会遍历这些HFile根据每个文件的RowKey范围找到对应的Region把文件从临时目录移动到Region的Store目录下同时把这些文件纳入HBase的元数据管理。整个过程对Region Server而言只是多了几个不可变的数据文件并没有触发逐条写入的开销。这也是BulkLoad为什么快的根本原因。第一阶段生成HFile的命令和Put模式很像区别主要在最后多了--hbase-bulkloadsqoop import \ --connect jdbc:mysql://192.168.1.10:3306/business?useSSLfalsecharacterEncodingutf8 \ --username root \ --password xxxx \ --table orders \ --hbase-table orders_hbase \ --column-family cf \ --hbase-row-key order_id \ --hbase-create-table \ --split-by order_id \ --num-mappers 12 \ --hbase-bulkload执行结束后数据并不会直接出现在HBase表里而是先落在HDFS上。可以检查一下输出目录HBase的BulkLoad输出路径通常和Sqoop的--target-dir或默认的/user/username/表名有关。确认HFile文件都存在后再执行第二阶段hbase org.apache.hadoop.hbase.mapreduce.LoadIncrementalHFiles \ /user/hadoop/orders_hbase \ orders_hbase如果执行成功再去HBase Shell里数数据就能看到导入的数据了。5.2 为什么BulkLoad比Put快这么多BulkLoad快不是偶然的它把数据写入过程里的多个瓶颈都绕开了。详细解释一下第一没有WAL同步写。Put模式下每条数据都要写WAL日志BulkLoad阶段根本不产生写入请求自然不需要写日志。第二没有MemStore刷写压力。Put模式大量写入时MemStore频繁达到阈值要刷写刷写期间Region要暂停服务。BulkLoad直接把数据以HFile形式写进HDFSMemStore完全不用参与。第三HFile文件本身是有序的。HBase底层存储就是有序的KeyValue序列BulkLoad生成的HFile天然就是有序的加载进Region后不需要再做一次排序整理。虽然加载后触发的Compaction还是可能重写文件但相比Put模式的逐条排序和写入已经省了非常多。第四写入路径从网络请求变成了文件移动。Put模式每条数据都要走一次网络BulkLoad只有一个文件级别的移动操作。我做过一次量化对比同样是导入2000万行订单数据Put模式跑了将近40分钟平均每秒不到1万行BulkLoad模式两个阶段合计只用了11分钟其中HFile生成阶段8分钟加载阶段3分钟速度大约是Put模式的3.5倍。数据量越大差距越明显到亿级数据时BulkLoad能轻松做到每几分钟刷完一个亿。5.3 加载阶段的三个关键细节BulkLoad的加载阶段有几个细节容易出问题我逐个说。第一个是表必须预分区。BulkLoad加载HFile时HBase会尝试把文件直接落入对应Region的Store目录。如果表只有一个Region所有HFile都挤在一起虽然不至于失败但后续的负载均衡会把数据重新分布效率依然很低。更关键的是如果表的Region边界和Sqoop生成的HFile边界不一致HBase加载时会拒绝接受报错信息通常是No transition to region之类的。解决办法是在建表时就规划好预分区让HBase表按照RowKey的分布范围提前创建好多个Region。建表命令类似这样create orders_hbase, {NAME cf, COMPRESSION SNAPPY}, \ {SPLITS [001, 002, 003, ...]}或者在Sqoop消费时用--hbase-create-table配合HBase的RegionSplitter工具做预分区。我在生产中一般用分段RowKey的方式把RowKey设计成[0-9a-f]前缀加业务id然后按前缀提前创建16个Region这样数据和Region边界天然对齐加载时几乎零摩擦。第二个是HFile和表结构必须匹配。HBase表如果改了列族名、列族的压缩算法、或者表层级结构有变化都会导致HFile无法加载。比如Sqoop生成HFile时列族名是cf但目标表实际列族是info加载时必然会报错。这种错误只要仔细对照建表DDL和Sqoop参数就能避免但出问题的人不在少数。第三个是压缩类型的一致性。如果目标表设置了Snappy压缩而Sqoop生成HFile时没有指定压缩类型或者用了其他压缩加载时HBase会按表定义重新处理但由于文件级别的元数据不一致也可能出现导入异常。稳妥的做法是让Sqoop的HFile生成参数和表定义保持一致。具体来说可以通过-D hbase.mapreduce.hfileoutputformat.compressSNAPPY来指定生成HFile时的压缩算法。5.4 增量加载和批量重复加载的处理实际业务里很少是一次性把数据全部倒完就完事。订单、日志这类数据往往是每天增量导一次这就涉及BulkLoad的增量场景。上次全量导入后表的Region边界已经被第一批数据撑开后续的增量HFile如果RowKey范围落在已有Region内部加载基本没问题。但如果有新数据超出了原有Region范围比如新增了9999开头的RowKey而之前预分区的边界只到9000加载时HBase会自动尝试split出新的Region来接收这部分数据。这个自动split行为有时会带来性能抖动稳妥的做法是在增量导入前主动观察表的最大RowKey必要时手动split一下Region。还有一个容易踩的坑是重复加载。如果因为作业失败或者其他原因同一批HFile被重复加载了两次HBase会做数据去重相同RowKey和相同版本时间戳的数据会被合并一般不会产生脏数据。但如果你用的是自定义时间戳重复加载时两次写入的时间戳不同旧版本的数据就会被保留下来查询时会看到历史版本有时候不是你想要的效果。所以加载前最好检查一下目标目录有没有残留文件有就清掉。6. 两个模式全维度对比该用哪个看这张表就够6.1 对比维度全景两种模式和几条关键维度的对比我直接汇总成一张表方便大家对照维度Put模式BulkLoad模式写入路径HBase API逐条写入走WAL和MemStore生成HFile直接加载绕开WAL和MemStore导入速度万级行/分钟数据量大时明显下降百万级行/分钟数据量增大性能衰减较小对线上集群影响高Region Server内存、CPU压力大可能触发频繁GC低文件加载阶段对集群内存影响小实现复杂度低Sqoop参数即可中需要预分区、加载步骤、注意列族匹配数据一致性实时写入失败后重跑即可加载阶段需确保数据不重复、不遗漏对表结构的依赖低表不存在可以自动创建高表必须预分区列族和压缩要和HFile一致实时同步支持支持可以作为持续同步方案不适合实时同步适合批量离线同步典型场景小数据量增量同步、临时抽数、Kafka无替代时历史全量导入、每日批量同步、亿级大表初始化6.2 从实战角度聊聊选择逻辑光看表格还不够我结合两个项目的实际经验聊聊选择逻辑。如果数据量在百万级以内Put模式完全够用。我做过一个标签系统的同步每天从业务库拉200万条标签数据写HBasePut模式跑了不到10分钟配置也简单出问题重跑也方便。这种情况下没必要上BulkLoad杀鸡不用牛刀。如果数据量到了千万级甚至亿级BulkLoad几乎没有悬念。我做过一个流量日志的归档项目每天大概8000万条日志要写进HBasePut模式跑下来要一个多小时而且整个Region Server集群的GC次数明显增多线上查询延迟都受了影响。切到BulkLoad之后生成HFile加加载总共不到15分钟集群完全没感觉。从那以后凡是超过500万行的离线导入我都默认走BulkLoad。还有一个特殊的场景是历史数据回刷。业务方想建一个长达一年的历史订单查询一次性导入上亿条历史数据这种场景Put模式基本不可用BulkLoad加上预分区加合理设计RowKey才能把整批数据在可接受的时间内倒完。6.3 集群资源角度也要纳入考虑选型不只要看导入任务本身还得考虑集群的承载能力。如果你的HBase集群还承担着线上实时读写业务导入作业的Put模式会直接影响线上查询的RT响应时间严重的会把Region Server打到GC Autopilot导致雪崩。这种情况我强烈建议把导入作业放到晚上低峰期或者干脆用BulkLoad模式把集群资源影响降到最低。反过来如果这个HBase集群就是专门的离线数仓上面没有线上业务那Put模式也能接受只是速度慢一些而已。所以说选型没有绝对的对错一切围绕你的数据规模、集群负载、业务实时性要求来权衡。7. 高频问题排查实录从装环境到跑任务7.1 sqoop连接不上mysql怎么办这个热词出现频率很高其实大多是三个原因。第一个是MySQL驱动jar没放到Sqoop的lib目录Connector/J这个jar没加载Sqoop会报No suitable driver found for jdbc:mysql://解决方法是下载mysql-connector-java对应版本jar放进Sqoop的lib目录同时注意版本和MySQL的兼容性。5.7的MySQL用5.1.49驱动基本不会出问题8.0的MySQL就要用8.0.x的驱动了直接拿老驱动去连8.0会报认证方式不支持的错。第二个是MySQL的host和port写错。生产环境MySQL通常有多个内网IP如果有防火墙3306端口从Sqoop所在机器访问不了也会连接超时。排查方法很简单先在Sqoop机器上直接mysql -hIP -P3306 -uroot -p试试能不能连上通了再跑Sqoop。连不上就查网络和防火墙策略。第三个是权限问题。MySQL账号需要有访问库表的权限只有SELECT肯定不够导入时Sqoop需要查询元数据有时还需要临时建表的权限。建议给Sqoop专门的账号只授权业务库的SELECT权限但要有SHOW DATABASES和SHOW TABLES的权限否则Sqoop的元数据探测过不去。7.2 跑BulkLoad时常见报错速查我把BulkLoad执行过程中容易撞上的报错整理成了一张速查表遇到问题可以直接对着查报错现象根因解决办法ClassNotFoundException: org.apache.hadoop.hbase.mapreduce.LoadIncrementalHFilesHBase的jar没在Sqoop/Hadoop classpath里把hbase-server、hbase-client等jar放到相应lib目录No transition to regionHFile的RowKey范围没有和表Region边界对齐确保建表时预分区覆盖所有RowKey范围Region too large单个Region数据量超过阈值检查预分区是否合理必要时增加Region数量Wrong column familyHFile的列族名和目标表不一致检查Sqoop参数和表DDL确保列族名一致File does not existHFile文件路径写错或已被清理确认Sqoop输出目录存在且文件完整7.3 HBase安装与配置中的基础问题热词里有一堆“hbase安装与配置”、“hbase端口清单”说明很多人栽在环境上了。简单说几个关键点。HBase依赖ZooKeeperHBase自身的HMaster和Region Server内部通信、客户端定位Region都需要ZooKeeper先启动且正常。HBase安装时最容易出的问题是版本不匹配HBase 1.x对ZooKeeper 3.4.x比较稳HBase 2.x对ZooKeeper 3.4.x和3.5.x都有支持但社区更推荐3.4版本。我在测试环境用ZooKeeper 3.5.9跑HBase 1.2.0时遇到过一些奇怪的Session Expired问题换成3.4.14之后清静了。关于端口HBase常用的端口清单如下端口作用2181ZooKeeper客户端连接端口16010HBase Master Web UI端口16020Region Server RPC通信端口16030Region Server Web UI端口16000HBase Master RPC通信端口9099Thrift Server端口用到Thrift API时如果你要用Java API操作HBase远程访问时要注意客户端连的其实是ZooKeeper的2181端口通过ZooKeeper拿到Region Server的地址然后再和16020端口通信。很多人在本地用Java程序连远程HBase只开了16010端口导致连接失败本质就是没搞懂这个连接链路。7.4 导入数据后查不到数据怎么办数据导入之后HBase Shell里数的行数和源表不一致也是常见困扰。原因有几个层面需要逐个排查。第一个是时间戳的问题。Put模式写数据时如果不指定时间戳HBase会默认使用当前服务器时间。如果导入过程中发生过任务失败重跑同一条数据的RowKey写入了两次但时间戳不同查询时默认只返回最新版本人为看起来像是丢了数据。BulkLoad模式生成HFile时同样会带上时间戳重复加载更要注意给同批数据指定统一的时间戳。第二个是列名的问题。Sqoop导入时默认列名就是源表的字段名如果你查询时用错了列名自然查不到。HBase Shell里用scan 表名, {LIMIT 10}看清楚实际存的列名再调整查询。第三个是RowKey的问题。这是最常见的。如果--hbase-row-key指向的字段在源表中不唯一后写的数据会覆盖先写的数据导致最终行数变少。在做全量导入时务必要选一个业务上唯一的字段或者用多字段拼接的方式构造唯一RowKey比如userId _ orderId。8. 最后聊点实战心得两个模式跑了这么久服过不少事最后分享几点体会。一个很深的感受是不要迷信“BulkLoad一定比Put好”。BulkLoad确实快但它的前提是你的表设计、预分区、RowKey全都到位不然HFile加载时依然会经历Region拆分和Compaction。我之前在一个项目里直接用默认的单Region表跑BulkLoad结果加载完成后HBase花了将近20分钟做Region拆分和负载均衡整体耗时反而比Put模式还慢。所以模式选型的前提是表设计过关否则什么模式都救不了。另一个体会是关于运维的节奏。BulkLoad加载HFile时如果一下子灌进去的HFile数量特别多加载完成后触发的Compaction风暴会让集群IO飙高。我的经验是控制单次导入的数据量比如一天的数据拆成几个批次依次加载批次之间间隔几分钟让集群有个消化时间。这个方法不花哨但很管用。如果你还想在这个方向上更深入可以考虑扩展几个点一是在Sqoop导入之前用Hive做一层数据清洗和转换让导入HBase的数据已经是最干净的形态二是结合HBase的协处理器在Region层面做数据聚合计算三是用Phoenix这类SQL on HBase的方案把导入后的表直接用SQL查询省去自己写Java API的繁琐。每个方向单拎出来都能写很长一篇这里就不展开了。希望这篇对你有帮助能让你在Sqoop和HBase集成的路上少踩几个坑。
返回列表