当前位置: 首页>后端>正文

Redis笔记(二)

高可用部署

主从 master-slave

Redis笔记(二),第1张

特点

  1. 读写分离:主节点写;多个从节点读
  2. 水平扩容:从节点水平扩容支撑读高并发
  3. 主从节点间的数据不保证及时一致性

部署建议:

  • 主节点做持久化,备份RDB,以防止主节点宕机重启后空数据状态被从节点同步后集群数据清空
  • master同步压力过大时使用主从从结构解决
  • 单节点内存分配尽量不超过10G
  • 为了Master的稳定性,主从复制不要用图状结构,用单向链表结构更稳 定,即主从关系为:Master<–Slave1<–Slave2<–Slave3...,这样的结构 也方便解决单点故障问题,实现Slave对Master的替换,也即,如果 Master挂了,可以立马启用Slave1做Master,其他不变。
  • 为了主从复制的速度和连接的稳定性,Slave和Master最好在同一个子网

核心原理:

  1. slave启动后---PSYNC---> master
  2. 若slave是初次连接master
    开始full resynchronization:
    master启动一个后台线程,开始生成一份 RDB
    master缓存新收到的client命令
    RDB 生成完毕后----> slave ----> RDB落盘---->加载RDB至内存
    缓存的client命令----> slave ----> 同步命令
  3. master ----client命令----> slave
  4. 断线重连后
    仅复制给 slave 部分缺少的数据

哨兵 sentinel

Redis笔记(二),第2张

特点

  • 集群监控:负责监控 redis master 和 slave 进程是否正常工作。
  • 通知告警:如果某个 redis 实例有故障,那么哨兵负责发送消息作为报警通知给 管理员。
  • 故障转移:如果 master 挂掉了,会选举 slave 为新master。
  • 配置同步:如果故障转移发生了,通知 client 客户端新的 master 地址。

部署建议:

  • 至少 3 个哨兵实例
  • 哨兵只能保证 redis 集群的高可用性

集群 Redis Cluster

摘自https://juejin.cn/post/6844904097116585991

Redis笔记(二),第3张

特点

  1. 无中心结构,所有的redis节点彼此互联(PING-PONG机制),内部使用gossip二进制协议优化传输速度和带宽
  2. 节点的fail是通过集群中超过半数的节点检测失效时才生效
  3. 客户端与redis节点直连,不需要中间代理层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可
  4. 每份数据分片会存储在多个互为主从的多节点上(支持配置为阻塞同步)

机制

  1. 每个节点有一个取值范围为0-16383的插槽(slot)
  2. 对key算CRC16结果对16384取余,以余找slot得节点,跳转节点取命令结果
  3. 为了保证高可用,Cluster模式也引入主从复制模式,一个主节点对应一个或者多个从节点,当主节点宕机的时候,就会启用从节点
  4. 当其它主节点ping一个主节点A时,如果半数以上的主节点与A通信超时,那么认为主节点A宕机了。如果主节点A和它的从节点都宕机了,那么该集群就无法再提供服务了

部署要点

  1. 端口开放redis命令端口A和A+10000端口(cluster bus)
  2. 扩容时时需要需要把旧节点的数据迁移一部分到新节点

优点

  • 无中心架构,支持动态扩容,对业务透明
  • 可线性扩展到1000多个节点,节点可动态添加或删除
  • 节点间互相监控,每个节点都保存各自的数据和整个集群的状态
  • 自动Failover(故障转移),节点之间通过gossip协议交换状态信息,用投票机制完成slave到master的角色转换

缺点

  • 运维复杂,数据迁移需要人工干预
  • slave充当“冷备”,不能缓解读压力
  • 批量操作限制,目前只支持具有相同slot值的key执行批量操作,对mset、mget、sunion等操作支持不友好
  • key事务操作支持限制,只支持落在同一节点的多key事务操作,多key分布不同节点时无法使用事务功能
  • 只能使用0号数据库
  • 分布式逻辑和存储模块耦合等

单节点分布式锁

摘自https://www.jianshu.com/p/1145cd7e0cf1

加锁

-- 加锁脚本,其中KEYS[]为外部传入参数
 -- KEYS[1]表示key
 -- KEYS[2]表示value
 -- KEYS[3]表示过期时间
 if redis.call("setnx", KEYS[1], KEYS[2]) == 1 then   --setnx: 不存在则添加
     return redis.call("pexpire", KEYS[1], KEYS[3])
 else
     return 0

解锁

 -- 解锁脚本
 -- KEYS[1]表示key
 -- KEYS[2]表示value
 -- return -1 表示未能获取到key或者key的值与传入的值不相等
 if redis.call("get",KEYS[1]) == KEYS[2] then
     return redis.call("del",KEYS[1])
 else
     return -1

RedLock

redis官方分布式锁

  1. 安全特性:互斥访问,即永远只有一个 client 能拿到锁
  2. 避免死锁:最终 client 都可能拿到锁,不会出现死锁的情况,即使原本 锁住某资源的 client crash 了或者出现了网络分区
  3. 容错性:只要大部分 Redis 节点存活就可以正常提供服务

系统缓存异常

缓存雪崩

指缓存同一时间大面积的失效,导致后面的请求落到数据库上,造成数据库短时间内承受大量请求而崩掉。

解决方案

  1. 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
  2. 一般并发量不是特别多的时候,使用最多的解决方案是加锁排队。
  3. 给每一个缓存数据增加相应的缓存标记,记录缓存的是否失效,如果缓 存标记失效,则更新数据缓存。

缓存穿透

指大量请求缓存和数据库中都没有的数据,导致所有的请求都落到数据库上, 造成数据库短时间内承受大量请求而崩掉。

解决方案

  1. 接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦 截;
  2. 从缓存取不到的数据,在数据库中也没有取到,这时也可以将key- value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会 导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力 攻击
  3. 采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对底层 存储系统的查询压力

缓存击穿

指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由 于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数 据库压力瞬间增大,造成过大压力。

和缓存雪崩的不同:
缓存击穿指并发查同 一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。

解决方案

  1. 设置热点数据永远不过期。
  2. 取数据库时加互斥锁,未获得锁则等待缓存更新

常用客户端

Redisson:对一部分Java对象进行了redis环境下的实现,代码成熟度高,面向业务开发比较友好
Jedis:提供了全部redis命令的api,对直接操作redis比较友好

缓存更新数据一致性

摘自https://www.huaweicloud.com/articles/52ba6b30c546d3a1e23b1e65e8150c64.html

基础方案:

  • 给缓存设置过期时间,保证最终一致性

不依赖过期时间的更新策略:

  1. 先更新数据库,再更新缓存
    [不推荐]
    并发导致缓存脏数据
    多写造成性能浪费
  2. 先删除缓存,再更新数据库
    并发导致缓存脏数据
    延时双删策略
    逻辑繁琐
  3. 先更新数据库,再删除缓存
    [推荐]
    可能产生脏数据的场景
    a.缓存刚好失效
    b.请求A查询数据库,得一个旧值
    c.请求B将新值写入数据库
    d.请求B删除缓存
    e.请求A将查到的旧值写入缓存
    完全解决小概率的脏数据问题
    流程
    (1)更新数据库数据
    (2)数据库会将操作信息写入binlog日志当中
    (3)订阅程序提取出所需要的数据以及key
    (4)另起一段非业务代码,获得该信息
    (5)尝试删除缓存操作,发现删除失败
    (6)将这些信息发送至消息队列
    (7)重新从消息队列中获得该数据,重试操作

https://www.xamrdz.com/backend/33v1945687.html

相关文章: