前几天出差,去客户现场帮忙迁移数据,通过几天的奋战,终于将迁移数据自动化起来,而且能够日跑批操做,这里小编就跟你们分享下,这其中踩过的坑(也多是实战经验不丰富致使)。
首先,荣小编我抱怨一下,不是本身熟悉的开发环境真的有些难过,给一台电脑,咱不说没有IDE,就连java都没有安装,链接数据库的工具也没有,惟一值得庆幸的是有xshell,可是彻底不符合我的快捷键的喜爱,没办法,想要开发高效,本身动手配置吧。单单是配置这些开发环境就整整牺牲了小编一上午的时间,还好后期开发有明显的提速。中午吃个饭,下午进入正题。
下午拿到迁移任务,发现一个库中有不少表,其中表有大有小,当时我沉默了几秒钟,感受这个星期是交代在这里了。这里小编在那几秒钟的沉默中也把这个迁移流程想了一下,首先数据是存在关系型数据库中的,而后咱们要经过sqoop将数据上传到HDFS中,而后用hive的外表去映射这些数据,最终创建有索引的内表来存储一份完整的数据。内表通常都是分区,分桶,且有索引的orc表,查询速度明显比外表快不少。那么接下来小编就将这其中的步骤,一点点的分析下。java
拿到一个库的数据时,咱们首先分析下这里有哪些表比较大,哪些表比较小,将大表和小表分开,使用不一样的迁移方法,通常都是客户提供每张表的数据条数,若是没有的话,只能selecct count(*) from table; 将这些表的数据查出来,不只便于区分大小表,并且对后期数据核对有较大的帮助。shell
这里迁移数据小编是用的sqoop,虽然sqoop比较慢,可是学习成本相对较低,并且便于批量的生成语句,对开发要求没那么高。首先先测试一个sqoop是否能够成功的迁移数据,而后编写脚本批量的生成sqoop语句,最后调用这些语句,后台并行的迁移数据。这里小编先说说使用sqoop的几个小窍门:数据库
-m 这个参数能够设置为>1 ,表示并行多个map去抽取数据。 --split-by 固然-m 参数设置大于后,要同时设置这个参数,表示以表中的哪个字段去分map并行。
这里选取--split-by 尽可能使用表中比较分散的字段,保证每个map任务抽取的数据量都大体相同。app
若是表的数据量比较大,好比超过亿条,咱们这里就须要将这个表分红多个sqoop去抽取:ide
--query : 指定where后的条件,抽取部分数据
这样的好处是:若是只有一个sqoop任务,抽取了90%的数据后,发现sqoop任务挂了,那么本次抽取失败,不只耗时,并且数据没有抽取到。分多个sqoop任务,不只可并行,并且每一个任务的数据量也不大,若是有任务挂了,只须要抽象抽取那个where条件下的数据即,而且对于找错也有极大的帮助。
分区字段的选取也一样重要,这里通常都是使用日期做为where的后的条件,保证每一个sqoop任务分的的数据量相差无几。工具
#小表目录规划 /tmp/库名/表名 #大表的目录规划 /tmp/库名/表名/分区名
query语句:在sqoop命令中,咱们编写查询语句去抽取数据时,切记不要:oop
-- ×
select * from table;
-- √
select 字段1,字段2.... from table;
否则可能会致使sqoop抽取速度变慢,甚至可能致使没有抽取到数据。
当咱们注意了以上的内容后,就能够编写脚本批量的生成每张表的sqoop语句了,根据库名.表名,获取关系型数据库中表的元数据,最后将sqoop组装起来。最后在编写任务脚本,定时执行这些sqoop语句。
实际数据分享:
这里小编测试过,数据量比较大时,多sqoop和单sqoop的耗时:
以600G数据为例:
- 多sqoop 并行抽取数据耗时:3~4小时。
- 单sqoop 抽取数据耗时:12小时以上。
- 单sqoop && (-m 1)抽取5千万条数据,大概是27分钟。学习
说白了就是将抽取到的数据,在hive中经过外表的方式映射出来,其实这里没什么难的,主要是看客户若是要求,多是外表单独一个库,或者外表的名称统一是:表名_ext。可是切记,不要手动的去编写建表语句,若是表有百张以上,心态容易炸,这里可使用关系型数据库的元数据 ,生成hive的建表语句的,这里咱们与MySQL为例:测试
SELECT CONCAT('create table ', TABLE_NAME, '(', substring(column_info, 1, length(column_info) - 1), ')', ' comment ', '"', TABLE_COMMENT, '"', ';') FROM (SELECT TABLE_NAME, TABLE_COMMENT, group_concat(CONCAT(COLUMN_NAME, ' ', DATA_TYPE, ' comment ', '"', COLUMN_COMMENT, '"')) AS column_info FROM (SELECT t1.TABLE_NAME, CASE WHEN t2.TABLE_COMMENT = NULL THEN t1.TABLE_NAME ELSE t2.TABLE_COMMENT END AS TABLE_COMMENT, COLUMN_NAME, CASE WHEN DATA_TYPE = 'varchar' THEN 'string' WHEN DATA_TYPE = 'int' THEN 'int' WHEN DATA_TYPE = 'tinyint' THEN 'tinyint' WHEN DATA_TYPE = 'decimal' THEN 'double' WHEN DATA_TYPE = 'datetime' THEN 'string' WHEN DATA_TYPE = 'timestamp' THEN 'string' WHEN DATA_TYPE = 'float' THEN 'double' WHEN DATA_TYPE = 'double' THEN 'double' WHEN DATA_TYPE = 'bigint' THEN 'bigint' END AS DATA_TYPE, CASE WHEN COLUMN_COMMENT = NULL THEN COLUMN_NAME ELSE COLUMN_COMMENT END AS COLUMN_COMMENT FROM COLUMNS t1 JOIN TABLES t2 ON t1.TABLE_NAME = t2.TABLE_NAME WHERE t1.TABLE_NAME = 't_app_equipment_status' ) t3 GROUP BY TABLE_NAME, TABLE_COMMENT ) t4;
网上这样的例子不少,这里小编就不在介绍。当外表创建好以后,最好核对一下数据量的大小,对比下关系型数据库中表的数据和hive中的数据是否相同,这样验证了sqoop这一环节是否有数据丢失的状况。
遇到的坑:
当大表咱们在分区抽取时,是没法直接映射成为外表的,咱们须要创建范围分区表,将表的分区目录映射到各个分区上。优化
其实这一步就是将,外表的数据,insert到一张和外表字段相同的通过优化的内表中,这张内表通常都是分区分桶,创建索引,或者基于闪存的表,反正就是查询的速度大大提升的一张表,也叫作业务表。
小编这里用的是一种基于闪存的高效查询的,企业内部开发的一种表结构。小编这里介绍一下如何肯定分桶字段:分桶的好处是
(1)得到更高的查询处理效率。桶为表加上了额外的结构,Hive 在处理有些查询时能利用这个结构。具体而言,链接两个在(包含链接列的)相同列上划分了桶的表,可使用 Map 端链接 (Map-side join)高效的实现。好比JOIN操做。对于JOIN操做两个表有一个相同的列,若是对这两个表都进行了桶操做。那么将保存相同列值的桶进行JOIN操做就能够,能够大大较少JOIN的数据量。
(2)使取样(sampling)更高效。在处理大规模数据集时,在开发和修改查询的阶段,若是能在数据集的一小部分数据上试运行查询,会带来不少方便。
那么若是肯定分桶字段呢,通常的若是有主键的表就使用主键做为分桶字段,若是没有主键的表,找几个比较分散的字段使用:
select count(distinct feild) from table;
找出数据最大的那个字段做为分桶字段。具体的分桶数,这里建议是一个质数,由于若是是一个非质数,那么可能致使分桶不均匀,由于若是分桶数是9的话,那么字段值若是为1八、27都会分到一个桶中,可能会致使桶“热点”。
这个过程是耗时仅次于sqoop的过程,因为咱们的内表创建的分区,那么在这个步骤中咱们须要使用hive的动态分区插入,插入语句通常都是:
insert into table_1 partition(par_field) select field1.field2...from table_2 ;
这里须要注意的是select 最后一个字段必定要是分区字段。
当咱们insert 后,须要核对下是否全部的数据所有insert成功,此时:外表数据量=内表数据量=关系型数据库数据量。
当咱们完成数据迁移后,其实外表至关于一个中转站,仅仅是将数据中转到内表中,若是咱们确保了内表中有一份完整的数据,此时能够将外表的数据清空,这也是为何咱们将外表的location 设置为/tmp下 的缘由,清空外表数据后,将日增数据抽取到外表的location地址上,而后全量的将外表数据insert到内表中,就保证了日增量数据的成功导入到hive。