缓存热key一旦被击穿,流量势必会打到数据库,如果数据库崩了,游戏直接结束。
从两点来讨论:如何监控、如何解决。
如何监控
- 通过业务评估:比如营销活动推出的商品或者热卖的商品。
- 基于LRU的命令,redis-cli --hotkeys,通过任务调度定时去获取?
- 基于redis客户端、或者代理层、或者redis监控策略,利用大数据领域的流式计算技术(如Storm、Spark Streaming、Flink)进行实时数据访问次数的统计,一旦发现热点数据,即可进行处理。
分析
为什么不直接访问数据库获取数据?
因为数据库主要是为了增量数据持久化,资源宝贵;库的并发连接数也有瓶颈;不好抗啊。
为什么要使用缓存?
缓存可以抗住海量并发;内存操作,速度快;
怎么保证缓存性能?
缓存预热;
缓存中间抗不住怎么办?
采用多级缓存,先访问本地缓存,再访问Redis缓存,减轻压力;增加Redis节点;
本地缓存需要注意些什么?
缓存核心(最)热点的数据;控制好缓存数据量级;堆内存啊,别OOM了;
缓存失效怎么办?
在访问数据库的时候加锁,控制流量,允许一个请求访问数据库,其他流量等待。
加什么锁?jvm锁还是分布式锁?
加本地锁就够了,数据库还没那么不堪一击,而且本地锁无网络开销、轻量快捷。
缓存哪些数据?
上面说了如何监控热点数据;关于动态的热点数据也需要实时监控?
还有什么兜底方案吗?
针对数据库流量进行限流,做最后的防护;
怎么做流量预估?
业务评估;全链路压测;
怎么保证可用性?
止血方案(开关、配置);监控;降级策略;扩容;
如何应对RPC线程池溢出造成的QPS瓶颈问题?
在客户端做一层缓存,同服务端缓存的逻辑一样。
如何解决
- 评估流量(redis和mysql是否能扛得住)
- 热key进行拆分,分散到不同的redis节点;增加redis节点
- 多级缓存,本地缓存 -》 redis缓存
- 如下策略:加锁+限流
伪代码
public class Main {private static final ReentrantLock lock = new ReentrantLock();private static final AtomicInteger retryCount = new AtomicInteger(0);public String getCacheValue(String key) throws Exception {try{String result = cache.get(key);if (result == null) {boolean b = lock.tryLock(200, TimeUnit.MILLISECONDS);if (b || retryCount.get() > 10) {String limit = limit.get(key);if (!limit.equals("限流")) {result = db.get(key);}} else {Thread.sleep(200);retryCount.incrementAndGet();result = this.getCacheValue(key);}}}finally{lock.unlock();}return result;}
}