中间件之缓存
- 缓存简介
- 缓存分类
- 缓存应用
- 缓存问题
- 缓存深入理解
- 总结
缓存简介
本章节我们的目的是弄清楚三个问题,什么是缓存,缓存用来干什么,缓存内部的算法和原理,首先提出几个我们日常开发中遇到的问题和现象
- 当我们从数据库读取大量数据java的集合无法承载,即使能也是对系统计算,和对内存的浪费,我们都知道在jvm中对于超大对象是直接进入老年代,yuangGC是无法回收年轻代的对象的,考虑到能提高计算能力,减少数据库开销,此时把数据放入缓存是非常必要的,
- 对于秒杀,抢购等高并发等场景,一方面由于对数据实时性要求比较高,另外为了可以扛着比较大的流量,此时提前把热点数据提前预热到缓存中,可以有效应对高并发,当然处理高并发和高可用不只是缓存设计,这涉及到整个系统层面的架构,但是缓存设计是其中重要组成部分,本章主要分析,深入了解缓存技术。
从上面的问题我们大致了解一下缓存是什么,当然上面只是简单介绍两个例子,所以我们大致能了解到 缓存其实一个中间组件,用来承接上层和下层之间的缓冲作用,其实系统设计中常用的到一种方法,如果两层之间不能解决实际问题的时候,我们通常通过增加一层或几层,用空间换取时间,对层与层之间进行拆分。
缓存分类
- 本地缓存
- 分布式缓存
- session,cookie
- CDN 静态资源存储技术
本地缓存主要有guava, caffeine分布式缓存有 redis,memcache,mongdb,session 主要用来存储服务端用户信息,cookie 用来存储客户端用户信息。 CDN是指 静态资源存储,主要用到nginx ,squid,varnish实现web静态资源缓存。
缓存之redis
-
缓存雪崩
所有key同一时间过期,所有请求打到数据库中,造成数据库宕机
解决方法,
1,设置可以不同的过期时间,如在过期时间加一个随机数。 -
缓存击穿
大量请求 请求同一个key ,热点key承受高并发,高负载 而失效造成请求打到数据库,造成数据库宕机,
解决办法:
1:热点数据永不过期。
2:接口限流,熔断,降级
3. 互斥锁
4. 布隆过滤器 -
缓存穿透
请求key ,key 在缓存中不存在,请求数据库也存在的现象
解决方法- 布隆过滤器
-
缓存持久化
AOF 记录操作的二进制文件,存储的数据比较全,用于全面备份恢复缓存数据,缺点是速度比较慢,
不会丢失数据
RDB 操作日志的快照文件,记录的数据没有AOF全,用于快速恢复数据,速度比较快,但是可能存在数据丢失的情况,通常是结合这两种持久化方式使用。 -
缓存淘汰策略
淘汰策略主要从以下几个方面去考虑, 随机,过期时间,频率
5.1. noeviction:当内存使用超过配置的时候会返回错误,不会驱逐任何键5.2. allkeys-lru:加入键的时候,如果过限,首先通过LRU算法驱逐最久没有使用的键
5.3. volatile-lru:加入键的时候如果过限,首先从设置了过期时间的键集合中驱逐最久没有使用的键
5.4. allkeys-random:加入键的时候如果过限,从所有key随机删除
5.5. volatile-random:加入键的时候如果过限,从过期键的集合中随机驱逐
5.6. volatile-ttl:从配置了过期时间的键中驱逐马上就要过期的键
5.7. volatile-lfu:从所有配置了过期时间的键中驱逐使用频率最少的键
5.8. allkeys-lfu:从所有键中驱逐使用频率最少的键
-
主从复制,sentinal, 集群
主从复制 可以分为 :同步复制 ,异步复制,全部同步,部分同步。
主从复制 支持一主多从,从服务器每隔一秒报告自己复制流状态,当从服务器初次同步,或者建立新的从从服务器,从服务器会发送一个sync命令给主服务器,主服务器收到命令执行BGSAVE 操作 把数据存储在.rdb快照文件中,如果此时主服务器还有中间操作,此时主服务器会把中间操作数据存储在一个缓冲区,当主服务器把.rdb 文件同步给从服务器之后在把缓存区的数据也同步给从服务器。即使多个从服务器会发送多个sync 命令但是主服务器只会执行一次bgsave操作,另外主服务器正在同步数据也不会阻塞主服务器其他写操作。除非是缓存更新的情况可能会阻塞其他操作。
sentinal (哨兵机制): 主要用到raft 算法,此算法主要用来选举leader,
sentinal 用于管理多台redis服务器,本身也是分布式的,主要有三个方面的任务,监控,提醒,自动故障迁移。
主观下线,客观下线。分布式架构中可以启动多个sentinel 进程,当只要有一个sentinel进程 捕获到lead 下线,此时进入主观下线状态,当只有超过半数的sentinel 都任务lead 下线之后,lead进入客观下线,进入自动故障迁移,首先通过raft 算法选举出 leader sentinel , 此算法首先淘汰 ping 超过五秒的从服务器,淘汰客观下线之后设定十倍的从服务器,再根据偏移量,和最少运行ID,保障只会 选举出一个leader
根据当前纪元自增之后,在当前纪元中选择出主服务器,通过 发布订阅模式把当前sentinel 配置跟其他sentinel 进行同步,同时从服务器也重新主服务器为刚当选的服务器,最后leader sentinel 终止这次自动故障迁移操作。 -
分布式锁
原则:可重入的,阻塞的,高效的获取锁,释放锁,
分布式锁是分布式系统中,保障数据一致性和同步操作的重要一环,通常有三种方式实现分布式锁,
1,利用数据库表锁机制,行锁,表锁,新建一张操作表进来记录操作数据记录,当开始操作是在此表插入一条新的数据,完成操作是删除这条记录,
缺点,对并发不友好,增加数据库额外开销,实现繁琐复杂,单点故障,不可重入,非阻塞的,
2:redis:利用redis setnx 的原子性操作,同时设置key 过期时间去释放锁。
3: zookeeper :创建临时节点,可保证最小的序号能在该持久节点下创建临时节点,同时保证当前线程获得锁,完成操作后只要删除 临时节点即可释放锁。
三种方案的比较
从理解的难易程度角度(从低到高)
数据库 > 缓存 > Zookeeper
从实现的复杂性角度(从低到高)
Zookeeper >= 缓存 > 数据库
从性能角度(从高到低)
缓存 > Zookeeper >= 数据库
从可靠性角度(从高到低)
Zookeeper > 缓存 > 数据库
缓存更新
主动更新缓存 ,三种,删除缓存再更新数据库,更新数据库再删除缓存再更新缓存,更新数据库更新缓存,需要考虑的中间过程是,如果删除缓存失败,或者数据库更新失败,或者更新缓存失败 同事考虑并发问题,会出现数据不一致的情况。此上三种情况在一定条件下都会出现数据不一致的情况,所以在分布式缓存中我们一般会设置,主服务器负责写,从服务器负责读,同时设置key合理的过期时间,当发生写比读数据快的情况的时候是可能发生数据不一致的。我们通常在日常中会选择 先更新数据库,然后删除缓存,然后再更新缓存这种模式。