1 Redis — 缓存方案(热数据和冷数据)
1.1 新建和更新数据时,设置一个过期时间product_cache_timeout(比如24小时), 查询时同时也将过期时间set in redis缓存
1.2 查到数据时重新设置过期时间redisUtil.expire —>缓存读延期
2 缓存击穿
缓存击穿是指:客户端查询不存在的数据,请求打到了存储层。
2.1 批量更新update数据时,超时时间同一时间是一样(解决:设置一个随机超时时间)
解决方案:
- 当需要从存储层查数据时,对请求加分布式锁,使得相同的请求在同一时刻只有一个能够打到存储层,同时从存储层查询到数据后将数据缓存起来;
- 设置缓存永不过期,当数据更新时动态的更新缓存;
- 采用多级缓存,如本项目再增加一级sql缓存
- 设置不同的缓存过期策略,本地缓存高频的热点数据(LFU),redis缓存短有效期的热点数据(LRU),redis缓存较长时间的全量数据。
3 缓存穿透
缓存穿透是指:客户端查询不存在查询的数据,使得请求直达存储层,导致服务器压力过大,甚至宕机。
3.1 删除缓存,DB数据。秒杀时查后端的所有数据,穿透后端
解决:查数据时is null时,给缓存设置empty_cache = ”{}”, 并且设置超时时间60s
解决方案:
- 查询存储层不存在的数据时,在缓存中插入空的值,当客户端再查询时,直接返回缓存中的空值;
- 使用布隆过滤器,将数据存入布隆过滤器,访问缓存之前先通过过滤器拦截,如果数据不存在直接返回空值。
4 突发性访问数据同一个产品(直播间买东西)DCL—> double check lock
4.1 synchronized(this){双重查缓存数据,再查DB} —> 仅适合单机,分布式要每台都要设置
5 不同产品,要设置分布式锁,解决突发性
5.1 redis 分布式锁(redisson)
格式: setnx key value
将 key的值设为value,当且仅当key不存在。若给定的key已经存在,则setnx不做任何动作。
解决:加一个redisson.getLock(lock_hot_cache,id); lock(); unlock();
6 缓存与数据库双写不一致
6.1 在update同一个产品加一个分布式锁。—>updateLock -> redisson.getLock()
7 优化分布式锁
7.1 读写锁(基于Lua脚本)
读锁:读锁是共享(不互斥,并发执行)
写锁:写锁不共享
Update同一个产品时加一个读写锁—> redission.getReadWriteLock();
7.2 串行转并发(是否需要根据业务需要) —> redissionLock.tryLock(3);
当大量请求访问,超过3s第一个请求还没处理完就退出,其它请求就要直接访问
8 缓存雪崩(上百万请求)
缓存雪崩是指:缓存中大量的数据同时过期,导致在某一时刻大量的请求直接打到存储层。由于缓存出问题导致DB也出问题,导致整个系统出问题。
8.1 多级缓存 —> 先查内存缓存(分发到多台服务器) —> Map
问题:多台服务器map缓存不一致
解决方案:
- 在缓存的过期时间后面加一个随机值,使不会有大量缓存在同一时刻过期;
- 采用降级或熔断措施,在发生雪崩时,直接返回预定义的值;
- 部署高可用的缓存集群,redis采用哨兵+集群的模式,部署多个实例,个别节点不可用时,依然保证服务整体可用。
更新缓存
当数据库发生修改时,会通过MQ的方式将修改的信息随机发送给一个provider,provider收到更新的消息后,会执行响应的缓存更新操作,更新redis缓存。当provider更新完redis缓存后,会再发送一条MQ消息,将更新信息广播给每一个consumer,consumer收到消息后更新本地缓存。