热key指的是,一段时间内访问频次比较高的key;
系统中引入、添加redis缓存,大概率是为了提高系统响应速度,同时保护下游相对脆弱的database;否则每次查询都走到database非常影响性能,流量大一些则会将database打挂,因此非常希望用户在访问系统时,能够命中redis中的缓存;
但当redis缓存中的某个key被高频访问,即出现热key,又会带来些衍生的新问题;
1. 带来的问题
由于key对应的数据一定是缓存(保存)在服务器redis单个实例中的,如果某一时刻出现热key,大量的请求操作会落到某台redis实例上,可能会造成redis实例所在机器的CPU使用率上升、网卡流量达到上限,进而影响机器及机器所搭载其他系统的稳定性和可用性,而当机器出现宕机情况,redis缓存不可用,最终这些请求会落到database上,database宕机也只是时间问题。
2. 广告投放引擎的案例
在某次618大促期间(活动抢购)站内广告系统曾出现过热key;
当时这台redis实例所在机器监控报警,CPU使用率和网卡流量飙升,由于无法连接生产环境(只能观察监控上机器的情况),紧急联系运维协助排查后发现是出现热key;好在当时机器的配置过硬挺过去了;
接着排查代码,发现没有为临时用户生成各自唯一的key,而是使用了统一的key,因此每当有临时(未登录)用户进入站点,广告接口调用缓存查找时就会重复使用同一个key,从而出现了热key。
3. 如何发现
理想的情况应该是我们能够及时发现热key,而不要等到机器报警或者实例挂了再去处理,道理大家都懂但理想很丰满现实很骨感,实际中大多都是机器报警了才发现;
预估
有些场景需要根据业务经验,而广告场景只要根据大促即可;
例如818一天中都有整点抢购活动,已经能提前预知当天流量会很多,那就预先将某些广告数据存入redis缓存中,如CPT包时段广告(这种广告不涉及个性化,固定时间、固定广告位);监控
通过自定义封装redis#client,实时统计key信息,当超过某个阈值时提前报警而不用等待机器层面的报警。
4. 如何解决
4.1. 冗余
冗余指的是,将同一份广告数据保存到多个的redis实例中,当流量来临请求广告时,流量可以均匀的分散到各个redis实例上。
但这并不适合所有广告数据,如CPT包时段广告(这种广告不涉及个性化,固定时间、固定广告位)可以冗余缓存。
但对于CPC竞价广告而言,由于其中牵扯广告竞价、算法(个性化、千人千面),注定无法长时间缓存用户的广告数据,否则每次展示给用户的都是同一组广告(影响收益),因此对CPC竞价广告来说,冗余这种方式是不现实的。
4.2. 本地缓存
或者说是JVM缓存,即服务程序自己内部的本地缓存。
通过自定义监控方式发现热key后,将其添加到服务程序的本地缓存(Guava Cache 或 Caffeine Cache),这样后续请求就查询本地缓存,拦截redis上热key的请求,缓解了redis实例压力。