并发情况下写入缓存
第一种方案:sleep(200) 睡眠 200 毫秒,重新到缓存中取数据,取到返回给客户端
第二种方案:直接返回空数据给客户端,提示稍后重试
典型的缓存击穿,缓存血崩案例。
https://github.com/golang/groupcache/blob/master/singleflight/singleflight.go
实现上也比较简单,可以看看上面贴的源码。用其他语言改写应该也问题不大。
两层含义:
1 缓存存在,但是里面的数值或者时间戳过期了,这种情况下可以先返回过期数据,然后另开一个线程去更新缓存。
2 缓存不存在了,最好避免这种情况以免数据库被击穿,可以另开一个循环线程去定期更新缓存。
也需要加锁的吧,比如 1000 并发请求,都发现快要过期了(例如 ttl<120 ),都去更新读数据库,效果其实和获取不到缓存数据的时候再更新是一样的。
大部分场景下热点数据其实就那么多,大部分是冷数据。所以目前有很多冷热数据的解决方案。这是另一个问题就不在这里讨论了。
主的问题是,业内常见的处理他不想用,正常查不到缓存就返回空前端处理一下,就留一个获取到锁的线程去更新。
主不想返回空,那么那么多线程在那里轮询类似自旋,就比较烦躁了。
还有一种方案就是 2 套 REDIS,一套过期时间长一些作为备份缓存,过期时间短的查不到去查这个备份的。
问题是 REDIS 在云服务商那不便宜啊,如果数据量一大成本是个问题。
如果工作者线程发现缓存没命中,这个数据也没在缓冲垫里,直接去数据库那数据就完了,然后一次性更新数量多一些的数据,如果有局部性可言。
如果工作者线程发现缓存没命中,这个数据在缓冲垫里,那就直接返回,先去做别的事务,等待已经去数据库取数据的工作者线程把数据取回来,再继续执行。
总之就是减少忙等,除非忙等的时间特别短。
一段时间没被访问的缓存走数据库是没问题的,但是要防备热点更新,这种比方说很多人每天早上醒过来的时候会看新闻这种。
你提的实际上就是 lru 算法的各种实现,搜搜看?