02|缓存一致:读多写少时,如何解决数据更新缓存不同步?
02|缓存一致:读多写少时,如何解决数据更新缓存不同步?
讲述:徐长龙
时长14:32大小13.28M
缓存性价比
临时热缓存
缓存更新不及时问题
1. 单条实体数据缓存刷新
2. 关系型和统计型数据缓存刷新
长期热数据缓存
总结
思考题
赞 14
提建议
精选留言(13)
- peter置顶2022-10-26 来自北京请教老师几个问题: Q1:缓存都是有超时时间的,从这个意义上说,都是“临时”的,为什么本文还要分为“临时”缓存和“长期”缓存? Q2:“临时”缓存和“长期”缓存在实现上可以用同一个软件吗? 比如,两者都可以用Redis实现?或者,“临时缓存”是用一个组件实现(非Redis)而“长期缓存”用Redis实现? 或者,“临时缓存”在代码中实现而“长期缓存”用Redis? Q3:人工维护缓存,怎么操作? 缓存数据一般都比较多,人工怎么能够维护一堆数据?具体是怎么操作的? 有一个界面,通过此界面来操作吗?展开
作者回复: 你好,peter,又见面了,感谢你的留言~ Q1:由于我们的大多数数据都是有时效性的,我们很少去做永久的内存缓存,毕竟内存还是很贵的,我们需要考虑性价比。长期缓存可以是一天,临时TTL是30秒。同时长期的更新是定期脚本刷新,临时是用到才会放进去一会儿。再来看,长期的访问很频繁,如果放开会导致数据库压力很大,但是临时的由于访问量小所以不用特意防击穿。 Q2:如果缓存压力不大可以用一个,如果很大会再做个L1缓存,在每台业务服务器上,这样能缓解核心缓存压力。 Q3:这个属于人工写代码,如我更新了用户的昵称,那么我会刷新这个用户的所有帖子的缓存,以及这个用户的所有最近留言缓存,以及这个用户的个人信息缓存。这是一种,还有一种就是你说的界面配置规则,但是这些都是要能快速定位的才可以。你可能会碰到,用户昵称以rick开头的账号有多少个,当我们改昵称为这个的时候,对于这种条件多样的,刷新哪个缓存不好确定,只能临时缓存30秒等他过期后刷新
共 2 条评论5 - Daniel置顶2022-10-27 来自北京1. 使用 Bloom Filter 识别热点 key 时,有时会识别失误,进而导致数据没有找到,那么如何避免这种情况呢? 通过我的 “机器学习的经验”,我觉得是这个布隆过滤器的哈希算法有点过拟合了,也就是说容错率高了,在资金充足的情况下先试着调低“容错率“(超参数)提升容量试试(不知道工业界上布隆过滤器的容错率能设置成 0%吗?但后期可能随着数据量的增长也是一个无限扩容的”吞金兽“呀),但我感觉我这个想法在工业界应该不成立。 第二种方法,我想到的是,如果这个 key 被误识别为”hotkey”的话,就在内存中记入“not_hotkey”列表,每次数据进来的时候,先用 缓存里的 not_hotkey里的列表来筛,要是不是hotkey就做成临时缓存,要是这个key是hotkey的话,就进行长期缓存来处理。 2. 使用 Bloom Filter 只能添加新 key,不能删除某一个 key,如果想更好地更新维护,有什么其他方式吗? 对于长时间不用的 key ,我认为可以设置一个“失效时间”,比如 一周内不用,就自动清除掉这个key。 之后在新的一周,把失效的key清理出去,再重新整理好一个列表,重新更新一遍这个布隆过滤器的新的哈希算法表。 (但感觉这个方法貌似不是最优的,也要在半夜用户量访问少的时间点去做变更处理) 老师想请教一个问题,对于 hotkey (热点数据)这个工业界的评价标准是不是不同行业会不一样呀? 比较想知道工业界上是用什么方法(一般统计方法?机器学习聚类?深度学习网络?)和工具(数据埋点?用户操作行为分析?),来做 “热点数据”的 判别的?展开
作者回复: 你好,Daniel,很高兴收到你的留言 第一种方法,如果使用集合检测也可以达到同样效果,你提到的方法缺陷在于not_hotkey会很大,这里需要记录所有不是hot key的所有key。目前bloomfilter只是个模糊筛选,用小量数据换更好的性能,但是他确实误判概率多一些。 第二个问题也是一个办法,但是需要我们能够精准控制这一批数据过期时间,但是我们在这节课用它主要是为了判断本地缓存中是否有这个缓存,由于无法判断所以需要每次询问,会导致系统更加复杂。 第三个问题 主要是访问量,我们可以将一些key 做一些count统计,数据埋点就足够了,机器学习和深度学习的QPS有些低,我印象里,一个1w元的显卡做发音评分,一秒钟只能处理4个请求,场景不太适合,有点小才大用了。
共 3 条评论2 - 传输助手2022-10-26 来自北京读取数据库设置缓存的时候,为了不受数据库主从延迟的影响,是不是需要强制读主库?
作者回复: 你好,传输助手,很高兴收到你的留言,这里有一个前提,就是我们加缓存的服务基本都是读并发高的服务,对于MySQL主库来说,问题就是全局只有一个主库,所以他是单点,同时更脆弱,理论上这种读压力尽量不要压到主库上~
3 - 一步2022-11-03 来自北京1. Bloom Filter 存在误报,会把不是热点的 key 识别成热点key, 所以需要一个 0误报的算法,数据结构 所以 Cuckoo Filter 布谷鸟过滤器来了 2. 可以定期或者其他策略 重新构造 Bloom Filter > 其实上面的2个问题,都可以使用 Cuckoo Filter 来解决
作者回复: 你好,很高兴收到你的回复,没错!确实Cuckoo Filter能够解决所有问题!同时补充提醒:他也有一些缺点使用的时候要注意,性能没有bf高,同时删除存在误删情况~
共 3 条评论2 - Elvis Lee2022-10-26 来自北京1. 使用 Bloom Filter 识别热点 key 时,有时会识别失误,进而导致数据没有找到,那么如何避免这种情况呢? 布隆可以判断一定不存在的数据,那么是否可以认为,只要插入不成功,即为热数据,但在设计布隆的时候需要根据业务来设置好容量和容错率。同时布隆删除操作在生产上不建议,最好是持久化后用版本号去区分。如果是离线链路,更推荐生成布隆文件,推送去客户端。实时的,目前接触是保存在Redis,Redis7的版本好像已经不需要插件展开
作者回复: 你好,Elvis Lee,很高兴收到你的留言,不建议这样使用bloomfilter,主要原因在于,他的识别和md5计算结果一样,有的时候不同的key返回的结果是一致的,所以这样是拿不到准确结果的。
共 4 条评论2 - hhh2022-11-11 来自北京没有抢到锁的sleep 1s然后去查询,这样接口耗时不是就会肯定大于1s吗 ,假如超时配置小于1s,这次请求不是必定会超时嘛
作者回复: 你好,hhh,时间不是绝对的这个可以根据情况进行调整,以前我们普遍是200ms左右,相对的这个方式比直接打沉中心数据库好那么一点,前提我们前端服务器足够多。
共 3 条评论1 - 一颗苹果2022-12-22 来自北京布隆过滤器的缺点,可以用hashmap那个链表解决,遇到冲突了延伸出一个链表,但得标注清楚这个元素是哪个key的。链表是空,直接set和普通布隆过滤器一样,不是空就追加一个元素(标注好哪个key)。删除的话遇到链表也遍历,根据匹配的key来删除。如果足够稀疏,那性能和一般布隆一样,数据越密集性能越下降。要准确性就只能牺牲性能来换吧
作者回复: 你好,一颗苹果,很高兴收到你的思路,这是个不错的办法。简单翻译一下,hashmap的hash环如果不够大,很多key都会碰撞在一个hash块内,而碰撞在一起的数据会在hashmap块内用链表保存,链表每次查找都要遍历所以会有一定性能下降,所以如果key少链表小的话会很快,当然这里没提及hash计算的时间复杂度。
- Geek_lucas2022-11-09 来自北京1、key-->hotkey布隆-->不存在-->非hotkey-->进入非hotkey布隆 2、key-->hotkey布隆-->存在-->可能是hotkey-->非hotkey布隆-->不存在-->可能是hotkey,当做hotkey处理 3、key-->hotkey布隆-->存在-->可能是hotkey-->非hotkey布隆-->存在-->可能是非hotkey,当做非hotkey处理
作者回复: 你好,是说另外记录一个key来记录他是否是hot_key吗
- Geek_lucas2022-11-09 来自北京1. 使用 Bloom Filter 识别热点 key 时,有时会识别失误,进而导致数据没有找到,那么如何避免这种情况呢? 答:学习数据结构的时候,我记得布隆过滤器,如果判断一个key命中,那么他【可能】有,如果判断一个key没有命中,那么他【一定】没有。所以应该反着来用,就是用来判断一个key,如果在hotkey布隆过滤器中没有命中,它一定不是hotkey。 2. 使用 Bloom Filter 只能添加新 key,不能删除某一个 key,如果想更好地更新维护,有什么其他方式吗? 无展开
作者回复: 你好,第一种有缺陷,因为我们的数据集很大,比如亿的key,第二种其实有办法只是代价选择
- SunshineBoy2022-11-01 来自北京哈喽 大佬 redis适合做app中一级、二级页面降级方案的存储吗?如果存储的value比较大,有没有推荐的降级方案?
作者回复: 你好,建议对象存储配合cdn缓存实现
共 3 条评论 - Sky2022-10-29 来自北京在更新期间,为了防止高并发查询打沉数据库,我们将更新流程做了简单的 singleflight(请求合并)优化,只有先抢到缓存更新锁的线程,才能进入后端读取数据库并将结果填写到缓存中。而没有抢到更新锁的线程先 sleep 1 秒,然后直接读取缓存返回结果。这样可以保证后端不会有多个线程读取同一条数据,从而冲垮缓存和数据库服务(缓存的写并发没有读性能那么好)。 并发更新的时候,为了防止超卖等问题,是不是最好还要在sql中加上乐观锁CAS?展开
作者回复: 你好,sky,很高兴收到你的留言,这样数据库压力会更大,这时需要特殊做,后续会讲到强一致怎么做
- 不吃包子2022-10-28 来自北京针对1.2的问题, 搜索到了如下解决方案: 调整布隆过滤器参数或者用布谷鸟过滤器。 我想说说我自己的看法,针对误判的情况,能不能再加一层缓存?比如说一个数据被误判为有,则去查询数据库了,这个时候为空,记到缓存里面,如果下次再访问该数据的时候,直接从缓存返回。针对问题2 同样也维护一个删除的缓存。
作者回复: 你好,不吃包子,很高兴收到你的留言,布谷鸟过滤器是一个不错的解决方案,另外查找不到缓存空也不错~
共 2 条评论 - 门窗小二2022-10-28 来自北京老师!但是课后题有答疑篇吗?
编辑回复: 建议先自己尝试回答,课后题答案后续看回答情况再公布。