關於hive事務表o狀態殘留問題的解決

上次我寫過一篇文章https://blog.csdn.net/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的事務能夠正常地發送心跳,保證長事務不會被異常淘汰。

具體的解決方法,我會在後面的文章中貼出來。

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章