关键词:redis slave spire 获取过期数据
周六晚会直播,有人反馈观看过广告后,再也不能触发广告了。第一次值班守护直播,就像守护女朋友一样,小心翼翼胆战心惊如履薄冰,怎奈还是翻船了。
话不多说,这锅我背了,快去找到原因解决问题吧。经过一番努力并没有头绪,经过项目组踩过坑的同事查证,redis cluster readonly=1, 导致了读取slave 过期expire数据的bug;
广告播放后,同一个用户接下来10分钟内不会再出广告。广告播放的标记存储在redis中,expire设置为600,按理10分钟后标记清除,广告系统获取不到播放标记会给用户再次下发广告。当晚有一些用户看过第一广告后,长时间无法第二次播放广告。经过查询相应用户后台日志,发现问题确实是10分钟不重复观看策略导致的。
也就是说redis存在expire过期数据仍可被读取的情况。
经过一番查证,redis曾发起Issue Improve expire consistency on slaves,以下摘录说明了这个情况(坑呀,宝宝心里苦%>_<%)
1 | In order for Redis to ensure consistency between a master and its slaves, eviction of keys |
凭什么认定我们是读取的从库呢?
翻出武功秘籍,对项目用到的golang redis.v5源码进行分析
1 | func (c *ClusterClient) cmdSlotAndNode(state *clusterState, cmd Cmder) (int, *clusterNode, error) { |
redis cluster的readonly字段配置为1的情况下,c.opt.ReadOnly条件成立,会使用slaveNode,反过来则使用masterNode,而使用slaveNode则可能引发上面的问题。
为什么我们测试的时候没有发现这个问题
话说我们也有测试过,几个人没有出现这个问题,脸黑吗(⊙o⊙)
找啊找,低版本Redis expire过期的策略在这里:
1 | How Redis expires keys |
从以上信息可以归纳出:
- 测试的时候,QPS小,Redis主动过期策略1s内可以清楚10*20=200个已过期的key,完全能处理测试好测试时候的expire key;
- 到了正式上线,QPS增大,整体上会保留25%已过期的expire key,这也可以解释为什么有些人可以重复看到广告,有些人不可以;
这种带有随机性质的问题,通常定位起来都会困难一些,脸确实有点黑O__O
解决方案:
- 查看我司服务器redis版本是redis_version:3.0.7-m,这个问题在Redis 3.2 中得到解决,升级大法保平安(万能解决之道,搞不定了,试试升级吧)。
- 结合go redis.v5库特性,将readonly字段配置为0,使用masterNode节点。当然,你可以直接连master,就不会有这个问题。但要注意这种方案将会增大master的压力,酌情考虑。
- 除此之外也有同学提出了另外的解决方案