为什么这么快?
纯内存
单线程,避免了多线程上下文切换
IO多路复用
应用场景
setnx实现分布式锁
zset实现延时队列
如订单超时未支付,取消订单,恢复库存。
生产者:新下的订单放入延时队列,score为到期处理时间。
消费者:用一个线程轮询延时队列,判断当前时间是否超过到期时间,若超出,则从队列中删除订单。
过期策略
定时
定期
惰性
redis:定期+惰性
内存淘汰机制
noeviction
allkeys-lru
allkeys-random
volatile-lru
volatile-random
votatile-ttl
持久化
底层数据结构
渐进式rehash
传统的hash表在扩容迁移数据(rehash)时,如果hash表数据量很大,一次性拷贝到新表中,务必耗费很长时间。
何为渐进式rehash?就是把拷贝节点数据的过程平摊到后续的操作中,而不是一次性拷贝。
缓存使用不当会有什么后果?
1.数据库与缓存数据不一致:
解决方案:canal等中间件同步binlog
2.缓存并发竞争:多个客户端并发同时写一个key
解决方案:
首先使用分布式锁(如zookeeper),确保同一时间,只能有一个系统实例在操作某个key,然后修改key的值时,要先判断这值的时间戳是否比缓存里的值的时间戳更靠后,如果是旧数据就不要更新了
3.缓存雪崩、缓存穿透、缓存击穿
解决方案:见下文
缓存雪崩、缓存穿透、缓存击穿
缓存雪崩:同一时刻大量key失效。
解决方案:1.设置随机过期时间。 2.限流
缓存穿透:总是查询一个一定不存在的数据,缓存始终无法命中,请求传到后台数据库。
解决方案:1.布隆过滤器;2.设置null值缓存
缓存击穿:热点key失效,导致大量请求到达后台数据库。
解决方案:
不同场景下的解决方式可如下:
若缓存的数据是基本不会发生更新的,则可尝试将该热点数据设置为永不过期。
若缓存的数据更新不频繁,且缓存刷新的整个流程耗时较少的情况下,则可以采用基于 Redis、zookeeper 等分布式中间件的分布式互斥锁,或者本地互斥锁以保证仅少量的请求能请求数据库并重新构建缓存,其余线程则在锁释放后能访问到新缓存。
若缓存的数据更新频繁或者在缓存刷新的流程耗时较长的情况下,可以利用定时线程在缓存过期前主动地重新构建缓存或者延后缓存的过期时间,以保证所有的请求能一直访问到对应的缓存