Cassandra通过写一条“tombstone”来标记一个数据被删除了。被标记的数据默认要10天(配置文件中的gc_grace_seconds)后且被compaction或cleanup执行到对应的SSTable时才会被真正从磁盘删除,因为如果当时这个delete操作只在3个节点中的2个执行成功,那么一旦2个有tombstone的节点把数据删了,集群上只剩下没tombstone的那个节点,下次读这个key的时候就又返回对应的数据,从而导致被删除的数据复活。Repair操作可以同步所有节点的数据从而保证tombstone在3个节点中都存在,因此如果想确保删除100%成功不会复活需要以小于gc_grace_seconds的周期定期执行repair操作(所以官方建议”weekly”)。
然而在select数据的时候,在每个SSTable遇到的所有符合查询条件的tombstone要放内存中从而在合并每个SSTable文件的数据时判断哪些column数据没被删能最终返回,tombstone太多会导致heap被大量占用需要各种GC从而影响性能,所以cassandra默认读到100000个(可配置)tombstone就强制取消这次读取,因为遇到这种情况一定是你“doing it wrong”。因此如果经常删除一个row key下的column key,同时又有一次select一个row key下多个column key的需求,很容易遇到这种情况,tombstone很多的时候即使不到10w最终成功读取了,这次读取也会很慢,并很快导致触发年轻代GC从而拖慢整个节点的响应速度。
最好的解决方案肯定是设计的时候就避免。但假如在不修改表结构的情况下,解决方案有两种:如果不能接受一个column key被删又复活,先把gc_grace_seconds改成0,然后删除的时候用ALL模式,当然只要有1个节点超时/挂掉删除就会失败;如果能接受被删的数据复活,删除只为了减少磁盘空间使用,那就直接把gc_grace_seconds设成0,复活就复活吧。