1 背景
随着公司业务的井喷式发展,数仓元数据也日益庞大,尤其是存在很多设计不合理的大分区表,导致一些元数据表(如PARTITIONS,PARTITION_PARAMS等表)高达数亿记录,这不得不让我们考虑做HMS的federation方案,另外,Mysql的稳定性也提出了更高的要求。本文正是在Mysql交由更专业的DBA团队运维的背景下产生的。
在Hive版本升级中, 需要考虑的是版本间的语法兼容性, 这应该是升级中最大的困难。
而元数据Hive MetaStore(HMS)的升级相对而言就简单很多,本文主要关注HMS的升级。
本文适用于想进行Hive元数据升级,尤其是想对Hive元数据Mysql做迁移改造的同学借鉴,也适于对Hive源码感兴趣的同学参考。
2 HiveSchemaTool源码分析
如果你亲手搭建过Hive,想必肯定对命令schematool -dbType mysql -initSchema
有印象,因为在搭建完Hive之后,执行该命令后,你会惊奇地发现Mysql中会创建好相应的元数据表信息。schematool
主要完成元数据库中表结构(如DBS,TBLS等)的更新创建。那它的背后是如何完成的呢?
2.1 schematool 的用法
你应该善于使用help:
schematool -help
usage: schemaTool
-dbOpts <databaseOpts> Backend DB specific options
-dbType <databaseType> Metastore database type
-dryRun list SQL scripts (no execute)
-help print this message
-info Show config and schema details
-initSchema Schema initialization
-initSchemaTo <initTo> Schema initialization to a version
-passWord <password> Override config file password
-upgradeSchema Schema upgrade
-upgradeSchemaFrom <upgradeFrom> Schema upgrade from a version
-userName <user> Override config file user name
-verbose only print SQL statements
dryRun是升级演习模式。可以打印出将要执行的SQL脚本信息。从而提醒我们该去哪个文件查看此次变更内容,让我们更清楚将要发生什么;
verbose参数可以打印出每个执行的SQL内容。 方便在失败时,我们知道是哪个环节出现了问题。
其他选项比较简单,不再一一介绍。
2.2 schematool 的功能
schematool主要有如下功能:
- 元数据版本升级
- 元数据初始化
- 元数据信息查看
2.3 核心代码分析
2.3.1 main入口函数
第一步参数解析
第二步参数校验
第三步强制开启版本校验
第四步参数赋值
第五步开始版本升级/初始化
源码流程比较清晰,不再多叙,可参考源码自行阅读。
2.3.2 upgrade
关于版本升级主要有两种方式: 不指定版本号和指定版本号
1 不指定版本号升级
upgradeSchema: schematool -upgradeSchema
默认不指定版本号升级, 这时是从mysql中读取版本号.2 指定版本号升级
upgradeSchemaFrom: schematool -upgradeSchemaFrom <upgradeFrom>
指定版本号升级,我们可以指定升级的版本号.这里可以是我们内部版本号
(如果是内部版本,可以使用指定内部版本号实现升级)
不管是否指定版本号升级, 最终都会调用方法:
org.apache.hive.beeline.HiveSchemaTool#doUpgrade(java.lang.String)
doUpgrade分析
1 等价版本号
合法的可识别的版本号保存在两个地方:
一是在硬代码静态变量中定义好的:
org.apache.hadoop.hive.metastore.MetaStoreSchemaInfo#EQUIVALENT_VERSIONS
这里定义的意思是说有些版本号之间的schema的变化是一致的,升级是等价的.
// 这里注明了一些等价的版本,也就是元数据没有变化的版本,如 1.2.1 和 1.2.0 版本的元数据是一致的。
private static final Map<String, String> EQUIVALENT_VERSIONS =
ImmutableMap.of("0.13.1", "0.13.0",
"1.0.0", "0.14.0",
"1.0.1", "1.0.0",
"1.1.1", "1.1.0",
"1.2.1", "1.2.0"
);
二是从数据库中获取版本号(这里是从VERSION表中读取版本号).
2 升级脚本注册文件
命名规则:upgrade.order.<dbType>
以mysql库为例:
upgrade.order.mysql:该文件中定义了mysql升级将要执行的脚本名标识。
# upgrade.order.mysql 文件内容:
0.5.0-to-0.6.0
0.6.0-to-0.7.0
0.7.0-to-0.8.0
0.8.0-to-0.9.0
0.9.0-to-0.10.0
0.10.0-to-0.11.0
0.11.0-to-0.12.0
0.12.0-to-0.13.0
0.13.0-to-0.14.0
0.14.0-to-1.1.0
1.1.0-to-1.2.0
upgrade.order.mysql文件中定义了升级脚本名标识,该标识以升级的当前版本号为前缀。这里也规定了升级的版本之间的依赖关系。如0.6.0版本,无法跨版本升级至1.2.0,只能通过逐级升级至1.2.0。
如何跨版本升级?
没错,聪明的你肯定想到了:我们可以通过修改该文件来实现跨元数据的版本升级。
如何自定义内部版本升级?
这里如果我们自定义内部版本升级, 我们可以在该文件中增加想要升级的版本标识,如:
1.2.1-to-1.2.1-stefan_r
,假定是mysql库,我们需要在scripts/upgrade/mysql目录下,定义内部版本升级脚本文件:
upgrage-1.2.1-to-1.2.1-stefan_r.mysql.sql
为什么是这个文件名呢?下面我们看升级脚本文件命名规则。
3 升级脚本文件命名规则
upgrage-<upgrade.order.mysql中定义的文件标识>.<dbType>.sql
4 升级脚本文件的获取
获取升级脚本文件的方法是:
org.apache.hadoop.hive.metastore.MetaStoreSchemaInfo#getUpgradeScripts
比如我们当前版本是1.1.0, 那执行如下命令
schematool -dbType mysql -initSchemaTo 1.1.0
, 那么会根据指定的版本号1.1.0 在upgrade.order.mysql 中找该版本号前缀的标识,将会升级到1.2.0版本(1.1.0-to-1.2.0)
当然,如果我们执行如下命令
schematool -dbType mysql -initSchema
该升级哪个版本呢?
此时的版本号是取<hive.version.shortname>1.2.0</hive.version.shortname>
, 这里不多展开。
5 升级脚本的执行
这里是利用beeline通过JDBC连接对应的数据库,完成升级脚本的执行。
这里有几个模式:
演习模式: 可以通过指定 -dryRun
开启。该模式不会真正地升级,会打印升级的文件。
verbose模式: 可以通过 -verbose
开启。该模式会将升级中执行的SQL打印出来。
通常不同issue对应的升级脚本都是有序独立提供,
主升级脚本中通常source他们,来看看 upgrade-0.14.0-to-1.1.0.mysql.sql 升级脚本的内容:
SELECT 'Upgrading MetaStore schema from 0.14.0 to 1.1.0' AS ' ';
SOURCE 020-HIVE-9296.mysql.sql;
UPDATE VERSION SET SCHEMA_VERSION='1.1.0', VERSION_COMMENT='Hive release version 1.1.0' where VER_ID=1;
SELECT 'Finished upgrading MetaStore schema from 0.14.0 to 1.1.0' AS ' ';
以上是升级upgrade中的一些细节,理解了升级,下面的初始化init理解起来就容易地多了。
2.3.2 init
初始化同样有两种方式: 不指定版本号和指定版本号
1 不指定版本号升级
initSchema: schematool -dbType -initSchema
默认不指定版本号升级, 这时是从mysql中读取版本号.2 指定版本号升级
initSchemaTo: schematool -dbType -initSchemaTo <initVersion>
不管是否指定版本号升级, 最终都会调用方法:
org.apache.hive.beeline.HiveSchemaTool#doInit(java.lang.String)
doInit分析
1 初始化脚本文件命名规则
hive-schema-<toVersion>.<dbType>.sql
2 升级脚本文件的获取
这个比较简单,根据初始化的目标版本号,获取。
其他都与upgrade类似。
3 元数据升级的一些注意事项
如果是不停服的升级,这里有两点需要注意:
一是要关闭版本严格校验模式。
如果在不停服的升级过程中可以做到对用户透明,避免客户端请求失败。
METASTORE_SCHEMA_VERIFICATION("hive.metastore.schema.verification", false,
"Enforce metastore schema version consistency.\n" +
"True: Verify that version information stored in metastore matches with one from Hive jars. Also disable automatic\n" +
" schema migration attempt. Users are required to manually migrate schema after Hive upgrade which ensures\n" +
" proper metastore schema migration. (Default)\n" +
"False: Warn if the version information stored in metastore doesn't match with one from in Hive jars."),
二是要关闭JDO框架自动更新元数据的功能(生产环境,不建议打开)。
因为元数据的更新由我们手工操作完成,这样更可控,我们更清楚发生了什么。如果打开该开关,JDO框架会默认创建缺失的表等。
METASTORE_AUTO_CREATE_ALL("datanucleus.schema.autoCreateAll", false,
"creates necessary schema on a startup if one doesn't exist. set this to false, after creating it once"),
最后别忘了在升级完成后,打开版本严格校验模式(当然也可以选择关闭)。
为什么我初始化之后default库没有自动创建?
有时候在initSchema之后,发现default库并没有创建,这是因为没有打开开关:
METASTORE_DEFAULT_DBROLES_INIT("hive.metastore.default.dbroles.init", false,
"If true, default db/roles are created and admin roles are added every time when metastore server starts"),
Thanks
54686973 20617274 69636C65 20697320 64656469 63617465 6420746F 20526F6E 67657220 77686F20 49206465 65706C79 206C6F76 65642E54 68697320 61727469 636C6520 69732064 65646963 61746564 20746F20 526F6E67 65722077 686F2049 20646565 706C7920 6C6F7665 642E5468 69732061 72746963 6C652069 73206465 64696361 74656420 746F2052 6F6E6765 72207768 6F204920 64656570 6C79206C 6F766564 2E