缘起
- 这篇文章的思考缘于今天的一个系统发布引起的缓存污染,所谓的缓存污染指的是缓存数据的字段内容因为新旧服务同时存在读写导致异常发生。
- 缓存污染造成影响一般包含两方面,一是数据反序列化失败;二是新增字段为null导致逻辑处理异常。
- 所谓反序列化失败是指新服务写入的缓存数据相对于旧服务而言增加了字段导致旧服务反序列化失败。
- 所谓新增字段为null导致逻辑处理异常使指新服务依赖核心新增字段但是由于旧服务写入的缓存数据不包含该新增字段导致异常。
- 当然常在河边走,这两种湿鞋的情况都遇到过,幸运未造成严重后果。
本质
- 缓存污染的问题基本上可以参考上面两种场景图,左边是两系统共用同一缓存的key,右边是同一系统的多台机器共用同一缓存key。
- 因为系统发布前后顺序的问题,会导致同时存在同一key两种类型value的写入和读取,如果不幸在序列化和反序列化当中没有做到向后兼容就会发生悲剧,同样如果有幸逃过序列化这个劫,还需要考虑针对新增字段为null的情况的兼容。
- 本质上针对缓存数据新增字段得保持两个意识,一是缓存数据序列化兼容问题,二是新增字段为null业务兼容问题。
- 缓存一旦被污染,要么期待缓存的数据TTL过期时间不长能够尽快恢复,要么有紧急开关能够一键关闭缓存直接访问DB,剩下的就是看天意了。
解决
- 针对涉及缓存数据格式更改的场景,特别需要关注数据内容的序列化和反序列化兼容性问题,如jackson需要特殊配置才能兼容,fastjson就不需要,具体的不确定可以实际验证一把。
- 针对序列化和反序列化部分建议增加try/catch进行兜底,保证序列化和反序列化失败当成缓存结果为空继续都DB访问,前期是DB能够扛住正常峰值流量。
- 缓存数据TTL设置切记过大,设置合理的过期时间能够让影响面尽量变小。
- 缓存污染是需要多台机器部署的场景才能出现的,但是测试环境和预发环境基本上都是单台机器,所以在测试过程中是很难发现的,这就是为什么上线总有意想不到的事情发生。
衍生
- 缓存和DB的数据一致性是业界难题,截止目前我了解到的组合解决方案就是 1、合理设置TTL保证数据过期;2、写完DB后删缓存由下次访问重新写入缓存;3、通过旁路的canal监听来实现兜底更新。