ZooKeeper 避坑指南: ZooKeeper 3.4.6 版本 BUG 導致的數據不一致問題

背景

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:https://issues.apache.org/jira/browse/ZOOKEEPER-2325

解決方案

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

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

MSE ZooKeeper 默認集成 Promethus 監控,提供豐富的指標信息,並且針對寫多的場景,MSE ZooKeeper 提供 TopN 大盤,能夠快速看到業務熱點數據,以及高 tps 的客戶端情況,能夠通過這些統計數據快速定位業務使用過程中的問題。

作者:子葵

原文鏈接

本文爲阿里雲原創內容,未經允許不得轉載。

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