ZooKeeper 避坑指南: ZooKeeper 3.6.4 版本 BUG 導致的資料不一致問題

阿里云云原生發表於2023-05-03

*作者:子葵*


## 背景


ZooKeeper 作為分散式系統的後設資料中心,對外服務的資料一致性需要得到很好的保證,但是一些老版本的 ZooKeeper 在一些情況下可能無法保證資料的一致性,導致依賴 ZooKeeper 的系統出現異常。


某使用者使用 3.4.6 版本 ZooKeeper 做任務排程,ZooKeeper 例項的 tps 和 qps 都比較高,事務日誌產生的速率很快,即使此使用者配置了自動清理的引數,但是自動清理的最小間隔還是趕不上資料產生的速度,導致磁碟爆滿。



在此使用者清理了舊日誌之後,重啟節點,部分業務機器就報出 NodeExist,NoNode 的異常,並且報錯只集中在部分機器,此次異常導致使用者任務排程系統出現任務重複排程以及任務丟失問題,產生重大損失。


## 原因分析


仔細檢查了這些客戶端發現這些客戶端都連線在同一臺 ZooKeeper 節點上,透過 zkCli 手動排查節點上的資料,對比其他未清理磁碟的 ZooKeeper 節點,清理了磁碟的 ZooKeeper 節點中的資料和其他節點具有差異,此時確定此節點由於一些原因出現了資料不一致問題,導致連線到此節點的客戶端讀到了髒資料。



但是排查日誌,沒有發現異常日誌。由於此節點之前清理過日誌,並且重啟過,磁碟上的資料被重新載入過,因此懷疑是 ZooKeeper 在啟動載入資料的過程中出現了一些異常情況。透過分析 ZooKeeper 啟動中載入資料的程式碼,繼續排查具體原因。



public long restore(DataTree dt, Map<Long, Integer> sessions,
            PlayBackListener listener) throws IOException {
        snapLog.deserialize(dt, sessions);
        FileTxnLog txnLog = new FileTxnLog(dataDir);
        TxnIterator itr = txnLog.read(dt.lastProcessedZxid+1);
        long highestZxid = dt.lastProcessedZxid;
        TxnHeader hdr;
        try {
            while (true) {
        ...
                try {
                    processTransaction(hdr,dt,sessions, itr.getTxn());
                } catch(KeeperException.NoNodeException e) {
                   throw new IOException("Failed to process transaction type: " +
                         hdr.getType() + " error: " + e.getMessage(), e);
                ...
        return highestZxid;
    }




此處是 ZooKeeper 載入磁碟資料的程式碼,此方法的主要作用是,首先將磁碟中的 snapshot 檔案載入進記憶體,初始化 ZooKeeper 記憶體中的資料結構,之後將載入事務日誌應用日誌中對資料的修改,最終還原磁碟中資料的狀態。



但是在 3.4.6 版本的程式碼中 snapLog.deserialize(dt, sessions);這行載入 snapshot 檔案的程式碼有一個返回值,此處沒有進行返回值校驗,導致在 ZooKeeper 本身找不到有效的 snapshot 檔案的情況下還是會繼續載入事務日誌,從而導致 ZooKeeper 在空資料的狀態下直接應用事務日誌,最終導致此節點的資料和其他節點的資料不一致。



此問題已經在 ZooKeeper 社群有對應的 ***issue***,在載入 snapshot 的檔案列表為空的情況下,此問題已經得到了修復,但是由於磁碟爆滿導致的 snapshot 檔案不完整的其他的一些特殊情況下,此問題依然存在。解決此問題還需要從磁碟使用的角度解決。


> ****issue:****

>

> *<*


## 解決方案


為了避免 ZooKeeper 節點的磁碟被快速打滿,可以增加磁碟的容量,配合 ZooKeeper 本身的清理機制,可以在一定範圍內的 tps 下避免磁碟被寫滿的情景,但是增大磁碟容量會帶來顯著的使用成本的提高,並且即使磁碟容量提高了,也可能因為 ZooKeeper 本身清理機制不及時清理,導致磁碟被打滿,最終需要透過人工的方式進行磁碟清理,運維起來很複雜,耗費人力物力,並且叢集穩定性得不到顯著提升。



MSE ZooKeeper 提供 ZooKeeper 例項的全託管,MSE ZooKeeper 例項的磁碟使用對使用者完全透明,使用者無需擔心磁碟爆滿問題,以及磁碟使用過程中的複雜運維。MSE ZooKeeper 透過定時清理,觸發使用閾值清理等手段保證 ZooKeeper 例項在使用過程中磁碟始終處於安全水位,避免由於磁碟問題導致的資料不一致,例項不可用等問題。



MSE ZooKeeper 預設整合 Promethus 監控,提供豐富的指標資訊,並且針對寫多的場景,MSE ZooKeeper 提供 TopN 大盤,能夠快速看到業務熱點資料,以及高 tps 的客戶端情況,能夠透過這些統計資料快速定位業務使用過程中的問題。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69953029/viewspace-2949707/,如需轉載,請註明出處,否則將追究法律責任。

相關文章