缓存
缓存更新方式
这是决定在使用缓存时就该考虑的问题。
- 设置缓存数据的TTL,当缓存数据失效后,如果有系统要请求,则会查询数据库并插入缓存(被动更新) 不友好
- 在各类会往mysql写入数据的系统中,植入更新缓存的逻辑(判断哪些表的数据是热点数据,那么就可以在这些表数据的更新操作逻辑中植入缓存刷新逻辑)
(严重弊端:将缓存更新逻辑与系统的业务逻辑高度耦合)后期维护很麻烦 - 异步更新:开发一个异步任务,定期从mysql数据库中读取“热门表”的数据更新缓存(时效性低)
- 利用canal监听mysql的binlog写入kafka,开发一个更新程序,消费kafka中的binlog信息,并根据“热度方案”进行缓存数据的更新;
(时效性强!对业务系统的mysql数据库几乎不带来任何压力!跟业务系统完全没有耦合,修改扩展维护都很方便)
数据不一致
第二个问题是数据不一致的问题,可以说只要使用缓存,就要考虑如何面对这个问题。缓存不一致产生的原因一般是主动更新失败,例如更新 DB 后,更新 Redis 因为网络原因请求超时;或者是异步更新失败导致。
解决的办法是,如果服务对耗时不是特别敏感可以增加重试;如果服务对耗时敏感可以通过异步补偿任务来处理失败的更新,或者短期的数据不一致不会影响业务,那么只要下次更新时可以成功,能保证最终一致性就可以。
缓存穿透
有人、系统,频繁查询数据库中不存在的数据;那么,缓存相当于不存在!
产生这个问题的原因可·能是外部的恶意攻击,例如,对用户信息进行了缓存,但恶意攻击者使用不存在的用户id频繁请求接口,导致查询缓存不命中,然后穿透 DB 查询依然不命中。这时会有大量请求穿透缓存访问到 DB。
解决的办法如下。
对不存在的数据,在缓存中保存一个空对象进行标记,防止相同 ID 再次访问 DB。不过有时这个方法并不能很好解决问题,可能导致缓存中存储大量无用数据
使用 BloomFilter 过滤器,BloomFilter 的特点是存在性检测,如果 BloomFilter 中不存在,那么数据一定不存在;如果 BloomFilter 中存在,实际数据也有可能会不存在。非常适合解决这类的问题
缓存击穿
就是某个热点数据失效时,大量针对这个数据的同时请求会穿透到数据源。
解决这个问题有如下办法。
可以使用互斥锁更新,保证同一个进程中的多个线程针对同一个数据不会并发请求到 DB,减小 DB 压力。但是查询的效率相对会低很多
使用随机退避方式,失效时随机 sleep一个很短的时间,再次查询,如果失败再查询mysql并更新redis。
缓存雪崩
缓存雪崩,产生的原因是缓存挂掉,这时所有的请求都会穿透到 DB。
解决方法:使用主从模式和集群模式来尽量保证缓存服务的高可用。
针对多个热点 key 同时失效的问题,可以在缓存时使用固定时间加上一个小的随机数,避免大量热点 key 同一时刻失效。