关于hive事务表o状态残留问题的解决

上次我写过一篇文章https://blog.youkuaiyun.com/u012543819/article/details/104540056,其中讲到,在hiveserver异常挂掉的情况下,可能会导致部分事务为o状态且残留在TXNS元数据表中。

通过仔细研究TxnHandler的代码,我发现其中有一个方法叫timeOutTxns,其实它核心也就是去修改TXNS表中超时的o状态事务为a状态,以便后续的合并任务不会被卡住。具体内容如下:

private void timeOutTxns(Connection dbConn) throws SQLException, MetaException, RetryException {
    long now = getDbTime(dbConn);
    Statement stmt = null;
    try {
      stmt = dbConn.createStatement();
      // Abort any timed out locks from the table.
      String s = "select txn_id from TXNS where txn_state = '" + TXN_OPEN +
        "' and txn_last_heartbeat <  " + (now - timeout);
      LOG.debug("Going to execute query <" + s + ">");
      ResultSet rs = stmt.executeQuery(s);
      List<Long> deadTxns = new ArrayList<Long>();
      // Limit the number of timed out transactions we do in one pass to keep from generating a
      // huge delete statement
      do {
        deadTxns.clear();
        for (int i = 0; i <  TIMED_OUT_TXN_ABORT_BATCH_SIZE && rs.next(); i++) {
          deadTxns.add(rs.getLong(1));
        }
        // We don't care whether all of the transactions get deleted or not,
        // if some didn't it most likely means someone else deleted them in the interum
        if (deadTxns.size() > 0) abortTxns(dbConn, deadTxns);
      } while (deadTxns.size() > 0);
      LOG.debug("Going to commit");
      dbConn.commit();
    } catch (SQLException e) {
      LOG.debug("Going to rollback");
      rollbackDBConn(dbConn);
      checkRetryable(dbConn, e, "abortTxn");
      throw new MetaException("Unable to update transaction database "
        + StringUtils.stringifyException(e));
    } finally {
      closeStmt(stmt);
    }
  }

我查看了调用他它的地方,在TxnHandler的getOpenTxns方法中。然后我们又持续跟踪的getOpenTxns方法的调用,发现合并时并未能够调用getOpenTxns方法,getOpenTxns方法是在DbTxnManager的getValidTxns方法中被调用,而getValidTxns的调用,在spark中并没有找到,因此,spark在产生新事务的时候,并不会去触发timeoutTxn是动作。 而合并时,hive是在Initiator的run方法中调用了getOpenTxnsInfo方法,

ValidTxnList txns = CompactionTxnHandler.createValidCompactTxnList(txnHandler.getOpenTxnsInfo());

然而getOpenTxnsInfo又并没有去调用timeoutTxns, 这就导致了如果遇到异常失败的状态o事务,那么就无法将失败的残留为o状态的事务状态修改为a,导致合并被阻塞。

我们只好在getOpenTxnsInfo中也调用了一下timeOutTxns,在合并前清理一下残留的o状态问题。 虽然这样听起来很合理,但是后面发生了意见诡异的事情。

我们在compact前timeoutTxns的方式听起来很合理,的确,这确实解决了事务的状态o残留的问题,但是却引发了另外的问题。

用户在使用的过程中发现,有时候执行delete操作后,数据就变成null了,数据丢失了,这是个非常严重的问题。

用户的操作流程大概是一个插入,更新,再删除的过程,简单表示如下:

insert  ->update ->(auto merge)->delete 

经过分析用户的数据,我们发现delete以后查询出的数据,非常像只读取了update的那部分delta文件的内容。除了update的字段以外,其余的字段都变成了null。我们通过查看spark-UI的job和分析spark的log发现,update 和delete操作中间有一次自动的major合并操作。

后面我们又神奇地发现, 在TXNs表中,状态为a的事务,事务的start_time 和last_heartbeat_time 都是同一个时间,也就是说,spark在执行事务操作时,未能正常地去发送事务心跳(为了验证这个问题我们专门构造了一个长事务去验证)。那么至此问题也就能够解释了:

长事务未能正确发送事务心跳,导致在合并时,被判断为timeout的事务,因此状态从o被标记为了a,合并时忽略了状态a事务的数据,恰好此处用户insert的数据非常大,是一个长事务,因此insert的数据在合并后被标记无效且删除了,就只剩下了update 的数据,导致很多列 数据都为null。因此,要解决该问题,需要去让spark的事务能够正常地发送心跳,保证长事务不会被异常淘汰。

具体的解决方法,我会在后面的文章中贴出来。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值