背景
Cloud Native
ZooKeeper 作为分布式系统的元数据中心,对外服务的数据一致性需要得到很好的保证,但是一些老版本的 ZooKeeper 在一些情况下可能无法保证数据的一致性,导致依赖 ZooKeeper 的系统出现异常。
原因分析
Cloud Native
仔细检查了这些客户端发现这些客户端都连接在同一台 ZooKeeper 节点上,通过 zkCli 手动排查节点上的数据,对比其他未清理磁盘的 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 内存中的数据结构,之后将加载事务日志应用日志中对数据的修改,最终还原磁盘中数据的状态。
此问题已经在 ZooKeeper 社区有对应的 issue,在加载 snapshot 的文件列表为空的情况下,此问题已经得到了修复,但是由于磁盘爆满导致的 snapshot 文件不完整的其他的一些特殊情况下,此问题依然存在。解决此问题还需要从磁盘使用的角度解决。
issue:
https://issues.apache.org/jira/browse/ZOOKEEPER-2325
解决方案
Cloud Native
为了避免 ZooKeeper 节点的磁盘被快速打满,可以增加磁盘的容量,配合 ZooKeeper 本身的清理机制,可以在一定范围内的 tps 下避免磁盘被写满的情景,但是增大磁盘容量会带来显著的使用成本的提高,并且即使磁盘容量提高了,也可能因为 ZooKeeper 本身清理机制不及时清理,导致磁盘被打满,最终需要通过人工的方式进行磁盘清理,运维起来很复杂,耗费人力物力,并且集群稳定性得不到显著提升。