23 | 缓存设计:缓存可以锦上添花也可以落井下石
该思维导图由 AI 生成,仅供参考
不要把 Redis 当作数据库
- 深入了解
- 翻译
- 解释
- 总结
本文深入探讨了缓存设计在系统性能优化中的重要性,特别强调了Redis作为缓存系统的注意事项。强调了缓存的作用是用空间换时间,可以加快IO并减少原始数据的计算工作。此外,还介绍了缓存雪崩问题和缓存击穿问题,并提出了解决方案。对于缓存穿透问题,提出了使用特殊值或布隆过滤器进行前置过滤的解决方案。另外,文章还探讨了缓存数据同步策略,强调了先更新数据库再删除缓存的策略是最佳选择。总之,本文为读者提供了系统性能优化方面的重要指导,对于技术人员在实际工作中具有实际的指导意义。
《Java 业务开发常见错误 100 例》,新⼈⾸单¥59
全部留言(25)
- 最新
- 精选
- Darren第一个问题: vivi童鞋回复的很棒,我的第一想法也是加随机后缀。 分型一个场景:假如在一个非常热点的数据,数据更新不是很频繁,但是查询非常的频繁,要保证基本保证100%的缓存命中率,该怎么处理? 我们的做法是,空间换效率,同一个key保留2份,1个不带后缀,1个带后缀,不带的后缀的有ttl,带后缀的没有,先查询不带后缀的,查询不到,做两件事情:1、后台程序查询DB更新缓存;2查询带后缀返回给调用方。这样可以尽可能的避免缓存击穿而引起的数据库挂了。 第二个问题: 1:单个key存储的value很大 key分为2种类型: 第一:该key需要每次都整存整取 可以尝试将对象分拆成几个key-value, 使用multiGet获取值,这样分拆的意义在于分拆单次操作的压力,将操作压力平摊到多个redis实例中,降低对单个redis的IO影响; 第二:该对象每次只需要存取部分数据 可以像第一种做法一样,分拆成几个key-value, 也可以将这个存储在一个hash中,每个field代表一个具体的属性,使用hget,hmget来获取部分的value,使用hset,hmset来更新部分属性。 2、一个集群存储了上亿的key 如果key的个数过多会带来更多的内存空间占用, 第一:key本身的占用(每个key 都会有一个Category前缀) 第二:集群模式中,服务端需要建立一些slot2key的映射关系,这其中的指针占用在key多的情况下也是浪费巨大空间 这两个方面在key个数上亿的时候消耗内存十分明显(Redis 3.2及以下版本均存在这个问题,4.0有优化); 所以减少key的个数可以减少内存消耗,可以参考的方案是转Hash结构存储,即原先是直接使用Redis String 的结构存储,现在将多个key存储在一个Hash结构中,具体场景参考如下: 一: key 本身就有很强的相关性,比如多个key 代表一个对象,每个key是对象的一个属性,这种可直接按照特定对象的特征来设置一个新Key——Hash结构, 原先的key则作为这个新Hash 的field。 二: key 本身没有相关性,预估一下总量,预分一个固定的桶数量 比如现在预估key 的总数为 2亿,按照一个hash存储 100个field来算,需要 2亿 / 100 = 200W 个桶 (200W 个key占用的空间很少,2亿可能有将近 20G ) 现在按照200W 固定桶分就是先计算出桶的序号 hash(123456789) % 200W , 这里最好保证这个 hash算法的值是个正数,否则需要调整下模除的规则; 这样算出三个key 的桶分别是 1 , 2, 2。 所以存储的时候调用API hset(key, field, value),读取的时候使用 hget (key, field) 注意两个地方:1,hash 取模对负数的处理; 2,预分桶的时候, 一个hash 中存储的值最好不要超过 512 ,100 左右较为合适
作者回复: 厉害
2020-05-0712100 - 汝林外史先更新数据库再删缓存,如果并发查询发生在删缓存之前更新数据库之后,查到的不都是旧数据吗? 不是应该先删除缓存,向队列中插入一个数据的修改标识,并发查询发现缓存为空把查询数据库的标识也放入队列中,等修改的处理完了再处理查询的请求。
作者回复: 我们的目标是避免长期出现不一致(读取到了旧值进入缓存属于长期不一致,因为又需要等一个缓存周期了)。先更新数据库再删缓存,如果并发查询发生在删缓存之前更新数据库之后,查到的不都是旧数据吗?是旧数据但是这是非常短暂的,下次查询就是新数据了。 你说的如何实现绝度一致。先删除缓存,向队列中插入一个数据的修改标识,我们如何确保删除缓存后向队列插入数据修改标识之前又有请求过来读取数据了呢?绝对一致或许考虑锁的方案。 不过反过来思考,既然已经缓存了,真的需要绝对一致吗?
2020-05-08525 - 赵宇浩跟用户信息相关的缓存怎么处理穿透,因为用户的量很大,很难全放进布隆过滤器。
作者回复: 测试一下 https://krisives.github.io/bloom-calculator/ 10亿的数据量,期望千分之一的错误率,推荐10个Hash函数,占用内存空间不到1.8GB
2020-05-07520 - vivi第一个问题,我认为可以给hotkey加上后缀,让这些hotkey打散到不同的redis实例上。
作者回复: 是的,加随机前缀后缀是一个办法
2020-05-07210 - wang对于一个频繁更新的热点key,有什么好的方案,先更新redis在定时同步到数据库,可能会丢数据
作者回复: 可以考虑一下Event Sourcing的思想,所有数据都从缓存读取,修改直接记录AppendLog+刷缓存,异步Apply到数据库,如果丟数据重放日志即可
2020-05-097 - hellojdMongodb算缓存吗?还是数据库?我们现正在开发的一个功能,将用户会员状态放到缓存中,缓存的过期时间是会员的过期时间,如果缓存没到缓存过期时间就被驱除,那就死翘翘了。
作者回复: mongodb是数据库,会员状态放缓存显然不合适
2020-05-115 - hellojd文稿中提到的布隆过滤器,怎么保证加载用户全部数据。用户量太大会不会oom?,如果新用户注册过来,怎么同步更新所有实例的布隆过滤器数据
作者回复: 布隆过滤器容量预估可以参考我之前的回答,如果数据保存节点内可以定时任务来加载新增数据,不同节点数据略有不同问题不大,或者也可以为redis安装布隆模块,集中保存
2020-05-114 - Michael老师,你好! 目前我们项目中遇到的问题: 当资讯在后台管理台修改完成审核上架后,需要在客户端实时生效,但是客户端的显示涉及分页和详情查询,分页查询中的缓存key涉及分页参数pageNo,pageSIze,分页和详情缓存key还涉及用户参数,这种情况情况如何处理?
作者回复: 我不是很理解你的问题,你是说咨询修改缓存怎么办?修改后可以直接把全量分页缓存的数据全部删除,也可以全量更新数据到缓存,当然,如果修改不涉及分页的改动,比如修改单篇文章的标题更新或删除那一页的缓存即可
2020-06-243 - 那时刻关于redis热key的处理,vivi和Darren给出很好的方案。如果hot key数据量不大的话,服务器本地localcache也是个方法吧?另外想问老师,在工程上如何发现热key呢?使用redis4提供的object freq?不知是否有其他方式?
作者回复: https://www.infoq.cn/article/3L3zAQ4H8xpNoM2glSyi
2020-05-083 - 码哥字节还有一种 “延迟双删” 的说法,也就是先删除缓存,再写数据库,延迟 一下再次删除缓存
作者回复: ok
2021-03-012