http://weibo.com/p/1001603788804719836993
故障那晚,也就是上周,Cassandra 集群无法响应请求,其中有一个节点CPU 超高,十六个核都跑满了。
嗯,我们用了Cassandra,不要问为什么,任性。。。节点们闹小团体,由于是gossip协议,流言漫天飞,新节点就是加不进去,据说已经几天了。于是手起刀落,把新节点干掉了。可惜,没起作用。(你以为钉子拔掉就没有坑了么?)
排查日志发现了异常 IndexOutOfBoundsException,报错代码在HintedHandOffManager.java#L516 参考:https://github.com/apache/cassandra/blob/5b878cebdfbd5ad1ea63eac5aa76f838b05fc038/src/java/org/apache/cassandra/db/HintedHandOffManager.java#L516
相关类只是一个简单的 UUID 生成类,Exception 原因很明显,生成 UUID 时候传入的 Key 字节数不足,因为UUID 生成算法是取传入key 的固定字节数据。这个hintsStore 是Cassandra在处理 replication 和 write 请求时,如果请求到达节点不是一致性哈希环上目标节点的话,当前节点会代为处理并在稍后将请求移交给目标节点。不过这个HintedHandOff操作在后台,默认10分钟一次,虽然每次都会刷新数据到磁盘,不像会对影响性能的地方。但为了尝试解决,我们使用 truncatehints 把hintsStore 干掉了。当然,依旧未遂。
(后来发现,这很可能是由于新加过节点又去掉了,其他节点存储的应该提交到新节点的数据由于节点退出而找不到。)
于是只好打开了当事DB 节点的Debug日志,发现这家伙在疯狂的Compaction,就是在努力把本地的SSTable文件整理成更紧凑的格式。参考 http://www.datastax.com/dev/blog/compaction-improvements-in-cassandra-21
老版本有两个阈值参数,分别是minimum/maximum compaction thresholds,新版本2.1加了max throughput limit,把这三个值分别搞成32、128、32后,情况没能立即好转,通过nodetool compactionstats 发现有个正在运行的 compaction 预计完成时间一小时。。。手工停掉Compaction之后情况似乎有些好转,但还是有百分之几的请求可能出现卡顿,注意不是timeout,而是一直停在那里。
针对这种偶尔的卡顿,经过谷歌的提醒,我们发现了新参数speculative_retry,嗯,总有惊喜在等待。这个重试参数跟 Google 的 Hedge Request 类似,就是在超过某个超时设置时,重新发送一遍请求,以期待新请求快速返回,消灭系统里的长尾效应。参考:http://www.datastax.com/dev/blog/rapid-read-protection-in-cassandra-2-0-2
默认设置是99percentile,可能由于停机时间太久,效果已经打了折,于是设置回10ms,系统终于恢复。。。当时是凌晨7点,办公室哭笑声一片。
## 我们能够学到什么
虽然是一个典型的故障处理,但其实暴露了很多问题,至少我个人这么认为:
1)技术选型慎用不熟悉的东西,特别是那些号称一揽子解决所有问题却没经过实践检验的产品;
2)如果实在想用,要选择其中源码公开且文档完善的。这次在代码上的排查帮助了快速判断,特别是针对那些 false positive 很重要;
3)如果实在要用,要有系统降级预案,最受不了的事情是看着某个服务出问题还束手无策的感觉,绝对是煎熬;
4)平时就多用谷歌,搜商很重要。