导图社区 软件开发缓存知识整理
软件开发中缓存知识点整理实战注意事项。包含缓存穿透、 缓存失效、 缓存雪崩、热点缓存key重建优化、过期策略等。
编辑于2024-03-04 16:26:34缓存
缓存穿透
缓存空对象
设置空对象过期时间
布隆过滤器
一个大型的位数组和几个不一样的无偏 hash 函数。向布隆过滤器中添加 key 时,会使用多个 hash 函数对 key 进行 hash 算得一个整数索引值然后对位数组长度 进行取模运算得到一个位置,每个 hash 函数都会算得一个不同的位置。再把位数组的这几个位置都置为 1 就完成了 add 操作。 当布隆过滤器说某个值存在时,这个值可能不存在;当它说不存在时,那就肯定不存在。
实现方式
redission
guava
缓存失效
失效时间增加随机数,打散过期时间,防止集中过期
缓存雪崩
高可用
哨兵模式
工作过程
通过向主服务器和从服务器发送ping命令, 让服务器返回运行状态
当哨兵监测到master宕机,会自动将一个slave切换成master, 然后通过发布订阅模式通知其他的从服务器,修改配置文件, 让它们切换主机。
三个定时任务
每1秒每个sentinel对其他sentinel和redis节点执行ping操作,心跳检测。
每10秒每个sentinel会对master和slave执行info命令,目的是发现slave结点, 确定主从关系。
每2秒每个sentinel通过master节点的channel交换信息(pub/sub)。 master节点上有一个发布订阅的频道(sentinel:hello)。sentinel节点通过__sentinel__:hello频道进行信息交换(对节点的"看法"和自身的信息),达成共识.
sentinel网络
sentinel是一个分布式系统,可以在一个架构中运行多个Sentinel进程。所以监控同一个Master的Sentinel会自动连接,组成一个分布式的Sentinel网络,互相通信并交换彼此关于被监视服务器信息。
网络故障修复原理
主观下线
当主服务器发生故障时,此时一个sentinel发现了故障, 系统并不会马上进行failover过程(这个现象称为主观下线), 它会向网络中的其他Sentinel进行确认
客观下线
其他Sentinel也陆续发现故障,这个时候其中一个Sentinel就会发起投票。 一定数量的哨兵(在配置文件中指定)确认Master被标记为主观下线, 此时将Master标记为客观下线。
sentinel的leader选举
要想完成故障切换(将故障master剔除,并将一个slave提升为master) 就必须先选举一个leader。最先发现故障的sentinel向其他哨兵发起请求成为leader, 其他哨兵在没有同意别的哨兵的leader请求时,就会把票投给该sentinel。 当半数以上的sentinel投票通过后就认定该sentinel为leader。 接下来的故障切换有该leader完成。
master选举(raft协议)
leader选好后将故障master剔除, 从slave中挑选一个成为master。 遵照的原则如下:
1、slave的优先级 2、slave从master那同步的数据量,那个slave多就优先。
新Master再通过发布订阅模式通知所有sentinel更新监控主机信息
故障的主服务器修复后将成为从服务器继续工作
数据丢失问题
CAP中的C和A无法同时兼容,目前的数据一致性C在逐渐地降低,已经不能容忍业务的需要,此时就需要降低A可用性来保证C,如果slave复制请求在得到ack超时达到 min-slaves-max-lag配置时候,并且有一定的slave数量,此时master对外终止响应。(分布式系统在实现CAP都是C和A的一个动态抉择,如:KAFKA的ISR列表为空用户可以选择使用OSR【降低一致性】)列表或者终暂停服务(降低可用性))
集群
可以动态扩充redis
特点
Redis集群预分好16384个桶,当需要在 Redis 集群中放置一个 key-value 时, 根据 CRC16(key) mod 16384的值,决定将一个key放到哪个桶中。
集群是一个整体,客户端与redis节点直连,不需要中间proxy层. 客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可
节点的失效是通过集群中超过半数的节点检测失效时才生效
所有的redis节点彼此互联(PING-PONG机制), 内部使用二进制协议优化传输速度和带宽
无中心结构,每个节点保存数据和整个集群状态, 每个节点都和其他所有节点连接
主从模式
奇数个节点
一个主节点对应一个从节点。主节点提供数据存取,从节点提供数据读取, 当主节点故障后,就会有这个从节点选取一个来充当主节点,从而保证集群正常运行
依赖隔离组件限流熔断降级 Sentinel或Hystrix
针对不同的数据 采取不同的处理方式
当业务应用访问的是非核心数据, 时,暂时停止从缓存中查询这些数据, 而是直接返回预定义的默认降级信息、空值或是 错误提示信息
访问的是核心数据时,仍然允许查询缓存, 如果缓存缺失, 也可以继续通过数据库读取
提前演练
演练缓存层宕掉后, 应用以及后端的负载情况以及可能出现的问题, 在此基 础上做一些预案设定
热点缓存key重建优化
缓存+过期时间 的策略 既可以加速数据读写, 又保证数据的定期更新, 可满足大部分需求
当前key是一个热点key,并发量非常大
重建缓存不能在短时间完成
当缓存失效瞬间,大量线程来重建缓存,造成后端负载加大
解决方案
通过互斥锁,只允许一个线程重建缓存 redis.set(mutexKey, "1", "ex 180", "nx")
高并发下 缓存与数据库双写不一致
缓存加过期时间,每隔一段时间触发读的主动更新
通过加读写锁保证并发读写或写写的时候按顺序排好队, 读读的时候相当于无锁
阿里开源的canal通过监听数据库的binlog日志及时的去修改缓存
场景
读多写少
增加缓存提高性能
写多读多
直接操作数据库
缓存作为主存储,异步将数据同步到数据库, 数据库作为数据的备份
过期策略
主库过期策略
定时扫描
每个设置了过期时间的key放到一个独立的hash中, 默认每秒定时遍历这个hash而不是整个空间
惰性策略
客户端访问的时候, 会对这个key的过期时间进行检查, 如果过期了就立即删除
从库过期策略
删除主库数据的时候,在aof文件里生成一条del指令, 在主从同步的时候,从库会执行这条指令,删除过期key 所以集群分布式锁算法的漏洞就是这样产生的
缓存失效策略
FIFO
最先进入缓存的数据在缓存空间不够情况下 (超出最大元素限制时)会被首先清理出去
LFU
一直以来最少被使用的元素会被被清理掉。 这就要求缓存的元素有一个hit 属性, 在缓存空间不够得情况下,hit 值最小的将会被清出缓存
LRU
最近最少使用的,缓存的元素有一个时间戳, 当缓存容量满了,而又需要腾出地方来缓存新的元素的时候, 那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存
缓存更新策略
开发规范
key
可读性、可管理性、简洁性
value
拒绝bigkey
导致redis阻塞
网络阻塞
过期采用异步删除(lazyfree-lazyexpire yes)
字符串类型
不允许超过10kb
非字符串
hash、list、set、zset元素个数不要超过5000。 反例:一个包含200万个元素的list
非字符串的bigkey,不要使用del删除, 使用hscan、sscan、zscan方式渐进式删除, 同时要注意防止bigkey过期时间自动删除问题(例如一个200万的zset设置1小时过期,会触发del操作,造成阻塞)
优化bigkey
拆
big list
big hash
取数的时候 hmget,而非hgetall