缓存设计
什么是缓存?
缓存的原始意义是访问速度比一般随机存储器快的一种高速存储器。
缓存的常见分类?
- 本地缓存
- 客户端缓存,客户端缓存主要包括HTTP缓存及浏览器缓存
- CDN缓存
- 反向代理缓存 Nginx缓存层
- 应用程序缓存 应用程序缓存是指利用应用程序来实现本地缓存机制。Ehcache\Guava
- 远程缓存
- 内存型缓存
- 持久化型缓存 将所需要的数据存储在SSD或Fusion-io等固态技术介质中。Memcacached是表追你的内存型缓存,而基于RocksDB开发的组件则属于持久型缓存。
如何正确选择缓存的读写策略?
Cache Aside Pattern 旁路缓存策略是最经典的缓存+数据库读写模式,分为读策略和写策略。
读策略 在读取缓存时先读取缓存 如果命中直接返回
写策略 先更新数据库 然后删除缓存
注: 先更新数据库,再更新缓存 是会造成数据不一致的。先删除缓存再更新数据库也会出现数据不一致的情况。不过这种现象很少发生,因为很少有写缓存比在更新数据库的同时删除缓存中的数据还慢,只要写入动作发生在删除缓存中的数据之前就没问题。
多级缓存存在哪些缺点?
热点缓存问题 在高并发场景下,该如何探测热点key。
- 热点缓存是指瞬间超高的并发请求打到缓存节点上,从而引起缓存节点不堪重负。如果有一台缓存节点负载过高 则很有可能会出现缓存节点宕机的现象。探测热点有两种方法可以实现.
- 离线计算 不停的分析历史数据。或者针对业务场景预测热点数据。
- 实时计算 采用Spark、Flink等技术 来实时统计数据的访问次数。
数据一致性问题 在多级缓存各层之间会存在数据不一致。
- 对于单层缓存系统 可以采用先删除缓存中的数据,然后更新数据库的方法来解决数据一致性,但是多级缓存当中如果采用这种方案逐层删除。会造成整体的性能下降。
- 如果对实时性要求不是很高 则可以采用全量同步+增量同步的方式进行
- 按照预计的热点key 对系统进行缓存预热,即全量同步数据到缓存系统
- 在需要个更新缓存时,采用增量同步的方式更新缓存。
缓存淘汰问题 对于缓存需要淘汰的数据 该如何设计过期策略。
- 使用FIFO算法来淘汰跟过期key
- 使用LFU算法来淘汰过期key
- 使用LRU算法来淘汰过期key
以上几种方案是数据达到最大缓存大小的淘汰策略。如果没有达到缓存最大缓存大小则有以下埃及中方式。
- 定时删除策略
- 定期删除策略
- 惰性删除策略
缓存架构设计
本地缓存组件有Ehcache\Guava Cache等,也可以使用Map、Set实现具有专有特性的缓存。
分布式缓存组件有Memcached、Redis、Pika等
Memcached的原理以及特性
Memcached在重启会丢失已经存在的数据,Memcached之间不进行互相通信。需要通过客户端程序来编写对key的hash算法来实现路由,实现分布式功能。
Memcached 的特点
- 高性能
- 访问协议简单
- 存储结构简单 只能存储简单的key-value
- 完全基于内存操作
- 服务节点运行简单
Redis的原理以及特性
Redis是基于Epoll事件模型开发的,可以进行非阻塞网络I/O。同时用于它是单线程处理的,且所有操作都在内存中进行,整个处理过程不存在竞争,所以不需要加锁,也没与上下文切换的开销, 所以Redis的性能非常高。
Redis 的特点:
支持数据䣌持久化,可以把数据持久化到磁盘当中
支持数据的备份 即支持Master-Slave模式的数据备份
支持原子性 Redis的所有操作都是原子性的,同时还支持对几个操作合并后的原子性
支持Cluster特性
除主进程外,Redis还会开辟一个子进程来处理负荷的工作。有3种情况会触发redis开辟子进程.
- 当收到bgrewritaof命令时,Redis会调用fork命令构建一个子进程,子进程向临时AOF文件写入重建数据库的所有命令
- 当收到bgsave命令时, Redis会构建一个子进程,子进程将内存种的所有数据他用过快照的方式持久化到数据库
- 当需要全量复制时,Redis会构建一个子进程,子进程将数据库快照保存到RDB快照文件种,在写完RDB快照文件后,Master节点会把RDB快照文件给Slave节点,且后续新的写指令都提供不给Slave节点。
Redis集群管理
- Redis集群管理主要有3种方式
- Client分片访问
- 使用代理
- 使用Redis集群
Redis和Memcached的区别?
在存储方式上,Memcached所有数据都放在内存上,重启数据会丢失,Redis 将部分数据保存在硬盘上,这样能实现数据的持久化
在数据类型的支持上Redis比Memcached多
在底层模型使用上,两者的底层实现方式及客户端通信的协议不一样 Redis自己构建了VM机制
在Value大小上 Redis可以达到1G 而memcached只有1MB
缓存分布的设计
- 分布算法?
- 由于受机器内存、网络带宽及单机请求量等方面的限制,所以单节点缓存很难应对高并发场景。可以根据分布式算法 将数据打散分布到不同的节点上,每个节点只存储一部分数据。
- 取余算法 对需要缓存的key先进行hash计算,然后对总缓存的节点进行取模计算
- 一致性Hash算法 可以解决取模算法种 再增加或者减少缓存节点时的命中率下降的问题。
- 在算法种 将整个Hash值空间组成一个虚拟的圆环
- 使用缓存节点的IP地址名 或者主机名 进行Hash计算,然后将计算得到的结果对2取模
- 取模的最终结果肯定是0到2之间的一个整数,即在Hash环上会有一个数与该整数对应。
- 当需要缓存对象时,先对这个对象的key采用同样的Hash计算将其映射到Hash环上,然后再Hash环上按顺时针查找。
- 一致性Hash算法在缓存命中率上影响较小,但也存在一些问题
- 缓存节点在哈希换上分布不均匀,这回造成部分缓存节点压力过大
- 脏数据问题。如果在将数据写入一个节点后进行更新时发生故障,则会偏移到另外一个节点上去操作。
- 读写访问实施
- 代码层开发,在代码client层感知分布策略,直接进行读写
- 利用代理来读写路由。client操作proxy,缓存分布算法逻辑及节点的部署变更都由Proxy来处理
缓存架构部署
- 分池访问: 对于核心的、高并发访问的数据,将其拆分到不同的缓存池中,进行分开访问,避免相互影响;对于非核心的数据和访问量较小的业务数据,可以将他们放在一起访问
- 分层访问 对于访问量到10w-100w级别的业务数据,最好进行分层访问,并且要分摊访问量,避免过载。
- 多IDC部署,如果业务系统被要求采用多IDC 部署,甚至实现异地多活,则缓存系统也需要采用多IDC部署
- 缓存组件组合,在某些极场景下,还需要组合多种缓存组件,通过缓存异构达到最佳读写性能
- 运维监控 站在系统层面,要更好地管理缓存,则需要考虑缓存地服务化,以及缓存体系如何更好地进行集群管理、监控运维等。
缓存架构设计的关键点
- 读写方式
- KV Size 如果单个业务i的KV size过大,则需要将其拆分成多个KV来缓存
- Key的数量 如果key的数量很大 则不能将缓存当作DB来使用。
- 读写峰值,对于读写峰值小于10w级别的QPS,可以使用独立的缓存池。
- 命中率 缓存命中率直接影响着系统的整体性能:命中率越高,则系统整体性能越高。
- 过期策略 通常对于缓存的数据时需要设置有效时间的。节省内存空间。做到数据弱一致性,即在有效期失效后可以保证数据的一致性。
- 平均缓存穿透 过载时间,在某些业务场景下需要关注平均缓存穿透加载时间。如果加载时很长,并且这些业务数据的访问量特别大,则需要配置更多存储容量来实现更高的命中率。
- 缓存可运维性 对于缓存可运维性,需要考虑缓存的集群管理。如何进行一键扩容,如何进行缓存组就按的升级和变更,如何快速发现并定位问题,如何持续监控报警
- 缓存安全性
- 限制IP地址
- 给部分关联指令增加一定的访问权限,以避免因核心数据被攻击或被误操作。
Redis主从复制的原理
在主从节点建立连接后,就可以进行数据同步了,即从从节点向主节点发送psync命令开始同步。
(1)全量复制 用于第一次复制,或者在无法进行部分复制时进行的复制,将主节点中所有数据发送给从节点
- 从节点在判断无法进行部分复制的时候,向主节点发送全量复制请求;或从节点发送的式部分复制的请求,但主节点判断无法进行部分复制。
- 主节点在收到全量复制的命令后执行,bgsave命令,在后台生产RDB文件,并使用一个缓存 区记录 "从现在开始i执行的所有命令"
- 主节点在执行完成bgsave命令后,将RDB文件发送给从节点;从节点先清除自己的旧数据,然后载入接受的RDB文件,将数据库状态更新至主节点执行bgsave命令时的数据库状态
- 主节点将之前复制缓冲区的所有命令发送给从节点,从节点开始执行写命令写并且将数据库状态更新至主节点的最新状态。
- 如果从节点开启了AOF 则会触发bgrewriteaof命令的执行。从而保证AOF文件更新至主节点的最新状态
(2) 部分被复制 在网络中断等情况后进行的复制,将中断期间主节点的写入的数据发送给从节点
- 复制偏移量 主节点和从节点分别维护一个复制偏移量,它代表的是主节点向从节点传递的字节数。
- 复制积压缓冲区 是由主节点维护的,固定长度的、先进先出队列,默认1MB。目的是备份主节点发送给从节点的数据
- 服务器运行ID 主从节点首次复制时候,主节点会将自己的runid发送给从节点,从节点会将这个runid保存起来。主节点根据runid来判断能否进行复制
- 如果从节点保存的runid与主节点现在的runid相同,则说明从节点之前通不过,主节点会尝试使用部分复制
- 如果从节点保存的runid与主节点现在的runid不同,则说明从节点在断线前同步的并不是当前主节点,所以只能进行全量复制。
缓存穿透了该怎么办?
- 回种空值
- 使用布隆过滤器
缓存雪崩了该怎么办?
通常是由于缓存体系中有较多的缓存节点不可以,则不支持rehash,所以请求会穿透到DB。从而导致DB不可用,最终导致整个缓存系统不可用。
缓存支持 rehash时的缓存雪崩。缓存支持rehash时产生的雪崩 一般跟瞬时流量洪峰有关。瞬时流量洪峰到达引发部分缓存节点过载,然后流量洪峰会扩散到其他缓存节点最终导致整个缓存系统异常
解决方案:
- 对DB访问增加读开关 当发现DB请求变慢 、出现阻塞、或者超过阈值,会关闭读开关,部分或所有DB的请求进行failfast立即返回。
- 给缓存系统增加多个副本。
- 对缓存系统进行实时的监控 。