并发情况下写入缓存

2021-02-02 08:43:30 +08:00
 rocky114
比如在 1000 并发的状态下,缓存过期了,这个时候需要读取数据库重新写入缓存,只有获取锁的线程才能读取数据库,其它没有拿到锁的线程如何处理呢?
第一种方案:sleep(200) 睡眠 200 毫秒,重新到缓存中取数据,取到返回给客户端
第二种方案:直接返回空数据给客户端,提示稍后重试
4742 次点击
所在节点    编程
46 条回复
vindurriel
2021-02-02 20:41:36 +08:00
两个方案刚好是 CAP 定理中选 C 还是 A 的问题 方案一选 C 问题是 200ms 不一定够 还得加随机数削峰 方案二选 A 增加了客户端 /使用者的负担
hxndg
2021-02-02 21:18:12 +08:00
我没实现过分布式,不过设计过单机的线程缓存操作之类的。。。。。提个自己的想法
创建一个缓存的队列,命名为缓冲垫,表示没命中,目前正在从数据库拿数据。

如果工作者线程发现缓存没命中,这个数据也没在缓冲垫里,直接去数据库那数据就完了,然后一次性更新数量多一些的数据,如果有局部性可言。

如果工作者线程发现缓存没命中,这个数据在缓冲垫里,那就直接返回,先去做别的事务,等待已经去数据库取数据的工作者线程把数据取回来,再继续执行。

总之就是减少忙等,除非忙等的时间特别短。
rocky114
2021-02-03 08:44:41 +08:00
@hxndg 这里是不允许多个线程直接读取数据库的,因为流量都直接到数据库容易把数据库搞奔溃了,所以需要增加一把锁,我这里的疑问点是其它拿不到锁的线程应该怎么处理?前排给出的方案是存储的缓存值增加 ttl 信息,这样每次读取缓存时判断下快过期的就重新设置一下缓存,这样就保证了缓存不会过期。可能有人会说一段时间没有访问缓存失效了,一下子并发上来还是会遇到问题,我认为一段时间没有访问的缓存不属于热点缓存,访问量应该不大。最后配合上凌晨缓存热更新应该能基本解决这些问题
sujin190
2021-02-03 10:29:41 +08:00
加锁就是了,搞个超高性能的锁服务,如果锁服务也挂了就返回让客户端重试,而且只需要在无缓存的时候才加锁从数据库加载,指单纯用于加锁的话,设计好搞个十倍 redis 性能的,妥妥的
sujin190
2021-02-03 10:32:07 +08:00
@xxy973211 #33 不过期有个极大问题是一致性维护太难了,写错了就麻烦死了,内存管理也很麻烦,缓存的话过了缓存时间就会从数据库加载,等同于系统有自动修复能力,维护会容易太多了
hxndg
2021-02-03 10:35:11 +08:00
@rocky114
如果单纯从缓存热点的角度来考虑,你使用 ttl 是可以做的,但是具体 ttl 的值哪种更合适这个只能测出来,因为不同的流量,ttl 的值可能需要动态变化。

一段时间没被访问的缓存走数据库是没问题的,但是要防备热点更新,这种比方说很多人每天早上醒过来的时候会看新闻这种。

你提的实际上就是 lru 算法的各种实现,搜搜看?

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/750489

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX