一文讲解Redis中热点Key的重建、可能会发生的问题及解决方案
一文讲解Redis中热点Key的重建、可能会发生的问题及解决方案
开发的时候一般使用“缓存+过期时间”的策略,既可以加速数据读写,又保证数据的定期更新,这种模式基本能够满足绝大部分需求。
但是有两个问题如果同时出现,可能就会出现比较大的问题:
-
当前 key 是一个热点 key(例如一个热门的娱乐新闻),并发量非常大。
-
重建缓存不能在短时间完成,可能是一个复杂计算,例如复杂的 SQL、多次 IO、多个依赖等。 在缓存失效的瞬间,有大量线程来重建缓存,造成后端负载加大,甚至可能会让应用崩溃。
怎么处理热key呢?
要解决这个问题也不是很复杂,解决问题的要点在于:
-
减少重建缓存的次数。
-
数据尽可能一致。
-
较少的潜在危险。
所以一般采用如下方式:
-
互斥锁(mutex key) 这种方法只允许一个线程重建缓存,其他线程等待重建缓存的线程执行完,重新从缓存获取数据即可。
-
永远不过期 “永远不过期”包含两层意思:
-
从缓存层面来看,确实没有设置过期时间,所以不会出现热点 key 过期后产生的问题,也就是“物理”不过期。
-
从功能层面来看,为每个 value 设置一个逻辑过期时间,当发现超过逻辑过期时间后,会使用单独的线程去构建缓存。
36.无底洞问题吗?如何解决?
2010 年,Facebook 的 Memcache 节点已经达到了 3000 个,承载着 TB 级别的缓存数据。但开发和运维人员发现了一个问题,为了满足业务要求添加了大量新 Memcache 节点,但是发现性能不但没有好转反而下降了,当时将这 种现象称为缓存的“无底洞”现象。
那么为什么会产生这种现象呢?
通常来说添加节点使得 Memcache 集群 性能应该更强了,但事实并非如此。键值数据库由于通常采用哈希函数将 key 映射到各个节点上,造成 key 的分布与业务无关,但是由于数据量和访问量的持续增长,造成需要添加大量节点做水平扩容,导致键值分布到更多的 节点上,所以无论是 Memcache 还是 Redis 的分布式,批量操作通常需要从不同节点上获取,相比于单机批量操作只涉及一次网络操作,分布式批量操作会涉及多次网络时间。
无底洞问题如何优化呢?
先分析一下无底洞问题:
-
客户端一次批量操作会涉及多次网络操作,也就意味着批量操作会随着节点的增多,耗时会不断增大。
-
网络连接数变多,对节点的性能也有一定影响。
常见的优化思路如下:
-
命令本身的优化,例如优化操作语句等。
-
减少网络通信次数。
-
降低接入成本,例如客户端使用长连/连接池、NIO 等。