记录日常工作中实际场景中,对hive分区表的一次启发与实践。
核心操作技巧
hive 指定分区locaiton,进行不同存储位置或协议的数据读取。
业务背景
在业务发展过程中,发现ucloud在某些峰值场景下会出现严重的数据问题,为了服务稳定性与存储可靠性,需要做整体服务云迁移的需求。
整体迁移技术背景:Ucloud -> Aliyun
当时设计迁移的方案:
计算集群与云存储服务并行,两边同时进行
Ucloud : 宿主机 + 自建 CDH + Ufile
Aliyun : EMR 服务 + Oss
云存储数据包括数据仓库中各个层级数据,业务、日志数据全部都有,数据迁移耗时很长。
对于历史底层数据的读取,完全依赖于两个云存储之间的数据迁移速度。
思考以及实践测试
虽然在最初的方案下完成了整体数据迁移工作,但是自我进行思考复盘的时候,偶然看到hive外表的灵活操作,如果把这个特性应用于当时的数据迁移中,会过渡的平滑一些,而且只需要保留一个计算集群即可。
目前数据迁移已完成,目前测试是基于Aliyun EMR 环境测试,读者如果有其他环境可以根据自己环境进行测试。
另外笔者不提供真实数据结果展示,只描述核心操作过程,因为整体测试表和数据是从真实数据和路径修改而成,为了保护数据私密性,望大家见谅。
正文开始
准备工作
测试表 order:
CREATE EXTERNAL TABLE test_db.`order`(
`id` bigint ,
`pay_id` string ,
`order_id` bigint ,
`settle_id` bigint ,
`original_order_id` bigint ,
`order_type` string
)
PARTITIONED BY (
`dt` string,
`hour` string)
ROW FORMAT SERDE
'org.openx.data.jsonserde.JsonSerDe'
WITH SERDEPROPERTIES (
'ignore.malformed.json'='true')
STORED AS INPUTFORMAT
'org.apache.hadoop.mapred.TextInputFormat'
OUTPUTFORMAT
'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
具体描述一下此表,此表为笔者实际测试表的部分数据,在建表时不指定location 的情况下,会默认指向hdfs存储地址,如下:
DESCRIBE FORMATTED test_db.`order`;
会在尾部显示location位置:
Location: hdfs://emr-cluster/user/hive/warehouse/test_db.db/order
Table Type: EXTERNAL_TABLE
至于数据格式,可以根据实际存储数据格式进行SERDE设置,笔者使用json格式数据。
实践过程
接下来可以设置不同分区指向不同地址。
ALTER TABLE test_db.`order` ADD IF NOT EXISTS PARTITION (dt = '2020-04-18', hour = '08')
LOCATION
'oss://datalake/order/dt=2020-04-18/hour=08'
;
ALTER TABLE test_db.`order` ADD IF NOT EXISTS PARTITION (dt = '2020-04-18', hour = '14')
LOCATION
'hdfs://emr-cluster/user/hive/warehouse/test_db.db/order/dt=2020-04-18/hour=14'
;
这样接下来就可以分别查询到存储在oss和hdfs上的数据。
select * from test_db.`order` where dt = '2020-04-18' and hour = '08';
select * from test_db.`order` where dt = '2020-04-18' and hour = '14';
此测试完成Hdfs 与 Oss 底层数据,在同一张外表的情况下,完成不同分区访问不同位置甚至不同云存储的数据。
预计 Hdfs 与其他云存储服务应该是相同的道理,只要hadoop配置了相应的云存储的配置,可以访问的情况下可以跨协议读取数据。
因为数据迁移已经完成,所以笔者在 Ufile 与 Oss之前的分别数据位置指定没有测试过。
其他场景实践测试
笔者使用此表,建表重新指向Oss的任意路径,例如:
CREATE EXTERNAL TABLE test_db.`order`(
`id` bigint ,
`pay_id` string ,
`order_id` bigint ,
`settle_id` bigint ,
`original_order_id` bigint ,
`order_type` string
)
PARTITIONED BY (
`dt` string,
`hour` string)
ROW FORMAT SERDE
'org.openx.data.jsonserde.JsonSerDe'
WITH SERDEPROPERTIES (
'ignore.malformed.json'='true')
STORED AS INPUTFORMAT
'org.apache.hadoop.mapred.TextInputFormat'
OUTPUTFORMAT
'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION
'oss://datalake/test'
另外说明一下,oss://datalake/test 路径下很多文件夹,但是没有dt= ,hour= 开头命名的文件夹。
将分区路径设置为指定位置后,最后的查询结果依然可以查询出来。
ALTER TABLE test_db.`order` ADD IF NOT EXISTS PARTITION (dt = '2020-04-18', hour = '08')
LOCATION
'oss://datalake/order/dt=2020-04-18/hour=08'
;
select * from test_db.`order` where dt = '2020-04-18' and hour = '08';
从实践结果来看,在跨越地址根目录进行数据查询的情况下,此方案仍然可行。
个人启发
- hive的分区灵活指定位置的可行性,在未来遇到数据迁移的场景下,提供了一条更加平滑、更加节省计算资源的迁移方案。
- 例如 A集群 迁移到 B集群,理想情况下 ,可以只保留 B集群的计算集群,保留A、B集群的存储即可。
- 而在实际操作过程中,值得特别注意的是,需要实际调研实践的是B计算集群访问A集群存储资源的可达性。
- 或者可以更加宽泛的看待业务场景,存在数据跨协议、位置读取的需求,此方案都可以作为一个备选项。