Hive0.13到2.1跨版本升级全姿势
前一段时间我们团队对Hive进行了一次从0.13版本到2.1版本的跨版本升级,升级期间也遇到了一些问题,
但是基本做到了可灰度
、可控制
和升级期间稳定性
保证。不停服务
这个属性通过本文分析也可以达到,
但是我们的场景下接受服务暂停,所以停服务花了一个小时来进行最终的升级。
使用背景:
深度
使用Hive的各项服务。- 线上每天SQL总量
80000+
,包含各种正常的和奇葩
的SQL语法。 - 同时使用
Spark
和Hive进行ETL
下面从元数据
、语法兼容性
、新功能
、UDF兼容性
、Hive2.1的一些不足
等几个方面来讨论下升级注意事项和升级期间踩过的一些坑。
1. 元数据
Hive元数据是Hadoop平台的核心数据,如果只是使用Hive提供的Hive Schema Tools
来进行元数据Schema升级的话,整个升级过程是黑盒,并不利于精确控制,在深度使用场景下还有可能爆各种奇葩错误。
例如如果你同时在用Spark,那么元数据VERSION表中的版本可能被修改为
1.2.1
(实际版本是0.13),如果
使用HiveSchemaMetaTools
时候参数不注意设定,这货是会1.2.1
开始执行升级脚本,最后会一部分升级脚本成功,一部分失败,然后结果你懂得。
所以首先需要对Hive元数据从0.13到2.1的升级脚本进行详细分析,再确定具体升级方式。
0.13到2.1的元数据Schema升级脚本,位于${HIVE_HOME}/metastore/scripts/upgrade/${DB_TYPE}/
文件夹内。
一共17个子升级脚本和对应的issue,通过查看每一个脚本明细SQL,和对应的issue信息以及适用场景,
可以将schema升级脚本分为以下几类:
升级脚本归类明细请参考
附录
,我们元数据使用MySQL数据库,使用其他类型数据库例如Oracle
,升级脚本有细微差别,请仔细review升级脚本。
1.1 建议修改:hive-13076(涉及到建表和修改表)
从代码分析上看不影响Hive正常操作,但是涉及到建表和修改表操作
,另外这个脚本的升级内容是一个建表操作,不涉及到修改表操作,所以风险较小,建议在灰度2.1客户端之前先进行升级操作。
这个升级目的是让hive支持表级别主键和外键,从而可以在CBO时候优化join的执行计划。
目前社区完成了主键和外键的语法支持和存储,而CBO使用主键和外键功能还处理未开发状态。
Hive2.1支持的新语法:
1.2. 事务(Hive Transaction)相关修改(不启用Hive事务则不需要)
默认事务是不启用的,当通过配置启用Hive事务时候使用到。Hive事务的应用场景比较有限。
1.3. Hcatalog相关修改(不使用Hcatalog则不需要)
增加NOTIFICATION_LOG
和NOTIFICATION_SEQUENCE
表。支持hcatalog metastore notification机制。
|
|
1.4. 其他非关键修改(一些特殊场景使用到)
|
|
总结
通过分析可以知道,0.13到2.1的元数据基本完全兼容,根据公司具体场景进行局部升级即可。
例如,如果不使用以上提到的事务、Hcatalog、Avro导出等特殊功能,完全可以做到只升级程序不升级元数据
也没有使用问题
。
但是前提是:
- 关闭元数据VERISON校验相关功能:设置
hive.metastore.schema.verification=false
。必要时候修改代码,禁用Hive自身的Check机制。 - 配置禁止JDO框架来自动更新元数据schema:设置
datanucleus.fixedDatastore=true
和datanucleus.autoCreateSchema=false
。
2. 语法兼容性
Hive版本升级后,给人的感觉是语法要求越来越严格
,所以很多在0.13可以跑的SQL,到了Hive2.1会报错。
下面总结升级过程中遇到的一些语法兼容性问题。
2.1 Hive新增保留字问题
Hive随着版本变迁,保留字越来越多。
保留字的详细情况可以参考:Hive各个版本的保留字
保留字问题有以下两种:
2.1.1 新增预留字
新的版本Hive会预留更多保留字,如果SQL中有问题,可以通过参数设置set hive.support.sql11.reserved.keywords=false
来取消保留字校验,
达到不需要修改SQL的又能保证线上SQL稳定运行的兼容效果。
2.1.2 新增关键字
当SQL中含有Hive中的关键字时候,只能修改SQL进行兼容了,需要将与关键字冲突的属性名用反引号包围。
新版本增加的关键字请参考附录1
。
2.2 其他兼容性问题和解决方法
hive.
开头的参数如果不在Hive2.1配置中(可能已经被移除),或者大小写不对,set语句会直接报错。
例如:set hive.auto.convert.JOIN=true
执行会报错。
解决方法:规范化语句,或者关闭强制校验参数。
- 建表时候不指定列的具体类型在2.1会直接报错。
例如:
解决方法:规范化SQL,指定具体的列类型
- 通过函数等运算产生的列,没有指定明确的别名,在2.1会偶尔报错。
例如:
解决方法:规范化SQL,指定有业务含义的别名,不以下划线开头
- 在使用windows函数的时候,当列名在多个表都存在的时候,不指定列所属表在2.1会报错。
例如:
|
|
其中user_id在a表和b表中均存在。
解决方法:规范化SQL,指定列所属表名。
- 在使用union all的时候,union起来的多个查询,含有
orderBy、clusterBy、distributeBy sortBy、limit
在2.1会报错。
例如:
|
|
解决方法:只允许在最后一个语句中含有orderBy、clusterBy、distributeBy sortBy、limit
语句。
- 使用windows函数时候,窗口的上限和下限必须大于
0
,否则在2.1会报错。
|
|
解决方法:规范化SQL,避免窗口函数上限和下限为0
,例如将0 preceding
修改为current row
case when
类型不一致,在2.1会报错
解决方法:将case when
里类型转换为一致。
2.3 总结
语法兼容性问题,可以提前拿到线上一段时间的SQL语句,启动一个2.1
版本的HiveServer2,
然后去批量Explain去校验是否存在语法兼容性问题,从而把问题解决到事前
。
我们这边的做法通过客户端自定义的Hook
和Dr-elephant
等工具能很方便的拿到线上所有的SQL语句,
同时将获取线上SQL
、批量Explain检查
、错误分析
等步骤,融合成一个Hive/Spark新功能上线前的回归测试工具来使用。
3. Hive2.1新feature
3.1 ORC&Parquet
Hive0.13对于2.1产生的ORC文件,存在读取兼容性问题。
hive2.xx之前,ORC存在一个UGI错误的bug比较严重,在HiveServer2里会遇到,
原因是ORCReader使用了多线程加载ORC文件的footer,而UGI的机制是不能跨线程的。相关issue:
另外hive2.xx之前,Parquet也存在诸多的bug。可以参考社区相关的issue。
文件格式的选择上,目前可参考的范例有:
- Uber: Parquert + snappy
- Linkedin: ORC + zlib
- Didi: ORC + zlib
- GrowingIO: ORC + zlib
我们倾向于主要文件格式选择parquet,因为ORC和Parquet测试下来速度差不多,但是Spark对于ORC的支持比较不好。
至于压缩算法的选择,我们倾向于对不同场景选择不同的压缩算法,
例如对数仓的ODS
层,数据量很大,使用频率很低,考虑使用zlib压缩算法,达到最高压缩比。
而DM
层,数据量小且访问频率高,则考虑使用snappy压缩算法,在压缩比和解压缩速度之间取一个tradeoff。
目前文件格式和压缩算法的选择正在测试上线阶段。
3.2 CBO
目前我们还在测试阶段。原因是CBO会在某一些SQL语句解析时候报错Assert Error
。
另外CBO的适用场景主要在于Join顺序的选择上,这个还需要在自己的线上场景上进行测试一番。
3.3 向量化执行
Hive的向量化执行目前只支持ORC文件格式,Parquet支持正在开发。在文件格式推广前效果不大。
3.4 Hive On Spark/Tez
目前看来社区还不成熟,目前发现只有Uber在维护和使用。
Tez目前更加冷清,之前用过一些效果还是很不错的,但是一直没有火起来。
我们的方法是用SparkSQL来加速部分的ETL语句,目前我们已经把SparkSQL
对原来HiveSQL的语法兼容性
和运行成功率
做到92%+
,这样让我们的线上ETL SQL可以更加平滑低成本进行迁移。
我们在Spark上进行的一些工作会有专门文章进行详细介绍。
3.5 其他
Hive2.1提供了大量的bug修复。除了上面提到的还有:
- HiveServer2的Heap和PermGen内存泄露问题。
- 大量存储类型bug修复。参考社区的issue list。
4. UDF兼容性
trim
函数在2.1不支持string之外的类型。
0.13支持,但是2.1不支持,想要兼容的拷贝0.13代码就可以解决。
date_add
和date_sub
函数2.1和0.13返回的类型不一致
2.1之前返回String
类型,2.1之后返回date
类型。如果where语句中有data_add
函数与String比较,可能导致数据查询不出来。
想要兼容的话,可以回滚到0.13版本的date_add和date_sub代码。具体请参考HiveUDF Date Functions
5. Hive2.1存在的一些问题
5.1 Hive Schema Version问题
如果元数据与Hive版本不同步升级,或用到Spark,因为Spark依赖的Hive默认是1.2.1。所以元数据也中的VERSION表,
会被改来改去,导致各种报错,例如:MetaException(message:Hive Schema version 2.1.0 does not match metastore's schema version 1.2.0 Metastore is not upgraded or corrupt)
一种方法是设置hive.metastore.schema.verification=false
。
还有一种彻底的方法是把Hive启动时候检查Schema的功能
屏蔽掉。
5.2 Metastore Server内存泄露问题
BoneCP+DirectSql开启时候Metastore Server内存泄露问题,
参考HIVE-15551,这个issue在2.2才完全解决,在Metastore Server端长期运行可能遇到,可以提前打patch来解决。
5.3 HiveServer2的多用户模拟问题
2.1后的HS2模拟多用户代码里,UGI的impersonation方式从CreateRemoteUser
变为CreateProxyUser
。
好处是可以获取到代理用户和被代理用户的信息,缺点是这种机制需要在Namenode端为每个被代理用户进行配置
。
具体请参考:Hadoop Impersonation
如果不想这么麻烦,可以从CreateProxyUser回滚到CreateRemoteUser的方式,可以参考Hive1.2.1代码。
5.4 HiveServer2的operationlog不打印到客户端问题
参考HIVE-14183,
设置hive.async.log.enabled=false
来解决。
5.5 Hive客户端PermGen OOM的问题
hive-env.sh设置export HADOOP_OPTS="$HADOOP_OPTS -XX:MaxPermSize=128m"
解决。
5.6 HiveServer2的性能问题
hive.driver.parallel.compilation=true
,默认为false,HS2只允许同时一个Query编译,
有操作元数据比较多的查询编译读取元数据会比较慢,会卡住所有其他查询。
打开允许多个Query同时编译。
5.7 基于Database的Stats收集策略被弃用
Hive2.1的Stats只能使用基于HDFS的策略,而StatsTask是单线程运行读取HDFS上的统计文件(文件数量等于Mapper数量),因为HDFS抖动导致了很多性能问题。
极端情况下,发现过一个statsTask会执行1个小时之久。
我们正在考虑把Stats Task
禁掉,或者切换回基于Database的Stats收集策略。
5.8 Column Pruning时候导致列顺序错误问题
Column Pruning时候导致列顺序错误,造成处理时候ArrayIndexOutOfBoundsException。
在一些复杂的SQL里增加limit会发生,参考HIVE-14564来解决。
5.9 我们Team回馈社区的一些patch
- Alter table Cascade时候的NPE问题:HIVE-16877
- Drop掉分区后执行insert overwrite报错问题(这个问题比较严重,有可能不报错,但是会引入脏数据):HIVE-17063
- 非当前库内Alter partition的异常问题:HIVE-17309
6. 其他一些踩过的坑
6.1 元数据数据库连接数打满问题
元数据直连数据库方式下,当并发服务量非常巨大时候,MySQL默认连接数是4000,比较容易打满。
解决方法:
直连使用BoneCP连接池方式下,
maxConnectionsPerPartition
默认值为10,一个客户端会建立20个连接,可以降低为2,并且降低连接池的活跃度。
可以考虑在$HIVE_CONF_DIR
下增加一个bonecp-config.xml
:123456789<?xml version="1.0" encoding="UTF-8"?><bonecp-config><default-config><property name="maxConnectionAgeInSeconds">5</property><property name="acquireIncrement">1</property><property name="maxConnectionsPerPartition">2</property><property name="lazyInit">true</property></default-config></bonecp-config>提升数据库连接数最大值,可能会有稳定性风险。
- 使用Metastore Server,这是比较通用的做法,一个Metastore Server扛
10000
个客户端连接,后端400个数据库连接毫无压力。
7. 升级流程总结
总结之前的分析,从Hive0.13升级到2.1,升级过程可以达到完全灰度和平滑
。
具体的升级流程如下:
- 元数据schema备份。
- 提前批量校验线上SQL,解决语法兼容性问题。
- 元数据只升级hive-13076中的脚本,建立
KEY_CONSTRAINTS
表和表上的索引。 - 定制Hive代码,根据使用场景解决2.1里面存在的UDF和其他的一些问题。
- 开始灰度升级Hive 2.1客户端,HiveServer2,最后是Metastore Server。
- Hive2.1客户端稳定后,根据需要部分或者全量来将元数据schema升级到2.1.1版本。
- 最后依次升级Hive JDBC客户端。(非必需)
8. 附录
8.1 Hive1.2之后新版本新增的关键字
|
|
8.2 Hive JDBC和HiveServer2各版本之间兼容性
name | server0.13 | server1.2 | server2.1 |
---|---|---|---|
jdbc0.13 | 兼容 | 兼容 | 兼容 |
jdbc1.2.1 | 兼容 | 兼容 | 兼容 |
jdbc2.1.0 | 兼容 | 兼容 | 兼容 |
jdbc2.1.1 | 不兼容 | 不兼容 | 兼容 |
8.3 从0.13开始的元数据Schema升级明细和影响分析
|
|