MetaStore服务启动时,“Metastore contains multiple versions (3) [ version = 1.2.0, comment = ...”的解决方法。

问题发现

某日夜里,收到线上业务报警短信,spark-client连接metastore服务失败,报出如下日志:

Caused by: org.apache.thrift.transport.TTransportException: java.net.SocketException: 断开的管道
        at org.apache.thrift.transport.TIOStreamTransport.flush(TIOStreamTransport.java:161)
        at org.apache.thrift.TServiceClient.sendBase(TServiceClient.java:73)
        at org.apache.thrift.TServiceClient.sendBase(TServiceClient.java:62)
        at org.apache.hadoop.hive.metastore.api.ThriftHiveMetastore$Client.send_get_database(ThriftHiveMetastore.java:660)
        at org.apache.hadoop.hive.metastore.api.ThriftHiveMetastore$Client.get_database(ThriftHiveMetastore.java:652)
        at org.apache.hadoop.hive.metastore.HiveMetaStoreClient.getDatabase(HiveMetaStoreClient.java:1187)
        at org.apache.hadoop.hive.metastore.client.impl.CombinedMetastoreClient.getDatabase(CombinedMetastoreClient.java:587)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.apache.hadoop.hive.metastore.RetryingMetaStoreClient.invoke(RetryingMetaStoreClient.java:152)
        at com.sun.proxy.$Proxy5.getDatabase(Unknown Source)
        at org.apache.hadoop.hive.ql.metadata.Hive.getDatabase(Hive.java:1311)
        ... 30 more
Caused by: java.net.SocketException: 断开的管道
        at java.net.SocketOutputStream.socketWrite0(Native Method)
        at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:109)
        at java.net.SocketOutputStream.write(SocketOutputStream.java:153)
        at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82)
        at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140)
        at org.apache.thrift.transport.TIOStreamTransport.flush(TIOStreamTransport.java:159)
        ... 43 more

但看这个日志看不出来什么问题,只好据以往的经验,先重启下metastore服务。

在metastore服务重启的过程中,报出来如下问题(日志中敏感的用户名/IP已脱敏):

Caused by: java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:422)
    at org.apache.hadoop.hive.metastore.MetaStoreUtils.newInstance(MetaStoreUtils.java:1521)
    ... 14 more
Caused by: MetaException(message:Metastore contains multiple versions (3) [ version = 1.2.0, comment = Set by MetaStore xx@xx.xx.xx.xx ] [ version = 1.2.0, comment = Set by MetaStore xx@xx.xx.xx.xx] [ version = 1.2.0, comment = Set by MetaStore xx@xx.xx.xx.xx ])
    at org.apache.hadoop.hive.metastore.ObjectStore.getMSchemaVersion(ObjectStore.java:6745)
    at org.apache.hadoop.hive.metastore.ObjectStore.getMetaStoreSchemaVersion(ObjectStore.java:6703)
    at org.apache.hadoop.hive.metastore.ObjectStore.checkSchema(ObjectStore.java:6661)
    at org.apache.hadoop.hive.metastore.ObjectStore.verifySchema(ObjectStore.java:6645)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)

说明的确是metastore服务出问题了。

问题排查

metastore的报错信息中提到【Metastore contains multiple versions (3) 】,根据关键字直接去hive的源码中搜索,在hive-metastore模块,org.apache.hadoop.hive.metastore.ObjectStore.java 中查到有这么一段逻辑:

 code中的逻辑是:去元数据库中查询version数据,如果查到的记录数 > 1,就会抛出【Metastore contains multiple versions 】的异常。

这就找到了线索,去元数据库中的VERSION表一看,的确是有三条数据(敏感信息已脱敏):

mysql> select * from VERSION limit 10;
+--------+----------------+-------------------------------------+
| VER_ID | SCHEMA_VERSION | VERSION_COMMENT                     |
+--------+----------------+-------------------------------------+
|      1 | 1.2.0          | Set by MetaStore xx@xx.xx.xx.xx |
|      2 | 1.2.0          | Set by MetaStore xx@xx.xx.xx.xx |
|      7 | 1.2.0          | Set by MetaStore xx@xx.xx.xx.xx |
+--------+----------------+-------------------------------------+
3 rows in set (0.00 sec)

mysql>

这样异常的原因就找到了:元数据库的VERSION表出现了多条记录(这个表正常是空或1条数据),导致metastore在初始化时,校验不通过,所以metastore服务启动失败,继而报出了文章一开始spark-client的问题。

解决办法

        删除VERSION表中的多余数据,只保留一条,重启metastore服务,发现metastore服务恢复正常启动,spark-client也可以正常连接了,故障修复,线上业务也恢复正常。

源码追踪:

        至于VERSION表中为什么会出现多条重复记录,这个需要先弄清楚VERSION表的数据是什么时候插入的。 在Hive的源码中(还是刚才的ObjectStore.java文件),有这么一个方法,是负责在metastore初始化时,往VERSION表中写入数据的:

@Override
  public void setMetaStoreSchemaVersion(String schemaVersion, String comment) throws MetaException {
    MVersionTable mSchemaVer;
    boolean commited = false;
    boolean recordVersion =
      HiveConf.getBoolVar(getConf(), HiveConf.ConfVars.METASTORE_SCHEMA_VERIFICATION_RECORD_VERSION);
    if (!recordVersion) {
      LOG.warn("setMetaStoreSchemaVersion called but recording version is disabled: " +
        "version = " + schemaVersion + ", comment = " + comment);
      return;
    }

    try {
      mSchemaVer = getMSchemaVersion();
    } catch (NoSuchObjectException e) {
      // if the version doesn't exist, then create it
      mSchemaVer = new MVersionTable();
    }

    mSchemaVer.setSchemaVersion(schemaVersion);
    mSchemaVer.setVersionComment(comment);
    try {
      openTransaction();
      pm.makePersistent(mSchemaVer);
      commited = commitTransaction();
    } finally {
      if (!commited) {
        rollbackTransaction();
      }
    }
  }

这个逻辑是:先从VERSION表里获取数据,如果已有数据,则进行更新,如果没有,则插入。

按理说,这个逻辑没有什么问题,而且上游方法还加了方法锁,应该不会出现重复插入的现象。

可现实情况的确是出现了数据重复的现象。这是什么原因呢?愁死个人了,找不到原因,没法跟领导解释呀。

脑子里突然想起一件事,这件事至关重要:我们的metastore节点是多节点部署的(当初由于任务量巨大,为了缓解单节点metastore的负载压力,所以搭建了多个metastore节点,连接同一个MySQL服务器,并配置了定期重启的机制)。突然就看到了黎明的曙光:单个metastore服务不会出现重复插入的现象,但是多个metastore会呀!如果在某个特定的时间节点,两台metastore节点同时进行初始化,同时走到了setMetaStoreSchemaVersion() 方法,就有可能出现同时插入数据的情况。(上游方法加的锁只能在自己这个服务内起作用,影响不到另外一个metastore服务)。

总结

        总结下故障发生的过程:

        1. 某个时刻,两台metastore节点同时重启,在初始化过程中都往VERSION表里插入了一条数据,造成VERSION表的数据条数 > 1;

        2. metastore 的初始化仍在进行后续的步骤,进行到getMetaStoreSchemaVersion()时,从VERSION表中获取数据,由于记录数 > 1 , 所以抛出【Metastore contains multiple versions (3)】的异常,导致metastore初始化失败,继而导致spark-client访问metastore失败。

        解决方法:

        1. 手动将VERSION表中的重复数据删除,重启metastore服务。

        2. 多metastore节点可能会重复插入VERSION表的问题貌似无解(虽然出现的几率很低),一个简单可行的解决办法就是把metastore定时重启的时间岔开,不要让两个metastore节点在同一时刻重启。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值