乐趣区

关于redis:Redis总结篇下

主从复制

主从复制是指将一台 Redis 服务器的数据,复制到其余的 Redis 服务器,前者称为主节点,后者称为从节点。

数据的复制是单向的,只能从主节点到从节点。
主节点以写为主,从节点以读为主。

  • 默认状况下,每台 Redis 服务器都是主节点。
  • 一个主节点能够有多个从节点
  • 一个从节点只能有一个主节点

为什么要主从复制

  • 主从复制实现了数据的热备份
  • 故障复原,当主节点呈现问题,服务能够由从节点提供
  • 负载平衡,尤其是在写少读多的场景下,通过多个节点分担读负载
  • 主从复制是高可用的基石

主从复制的外围机制

  1. Redis 采纳异步形式复制数据到 slave 节点,Redis2.8 开始,slave node 会周期性地确认本人每次复制的数据量
  2. 一个 master node 能够配置多个 slave node
  3. slave 能够连贯其余的 slave
  4. slave 在复制的时候,不会阻塞 master 失常工作
  5. slave 在复制的时候,不会阻塞本人的查问操作,复制实现后加载数据时会暂停对外服务

全量复制 | 增量复制

残缺同步:

当一个 Redis 服务器接管到 replicaof 命令,开始对另一个服务器进行复制的时候,主服务器会进行如下操作:

  • 主服务器 bgsave 生成一个 RDB,缓存区存储 bgsave 命令之后的写命令
  • 主通过套接字传 RDB 给从
  • 从接管载入 RDB 文件
  • 主再把缓存区的写命令发送给从

主机中的所有信息和数据,都会主动被从机备份保留。

全量复制:slave 服务在接管到数据库文件数据后,将其存盘并加载到内存中
增量复制:master 持续将新的所有收集到的批改命令顺次传给 slave,实现同步

外围原理

Slave 启动胜利后,会发送一个 psync 同步命令给 master。

如果这是 slave 首次连贯到 master,会触发一次 full resynchronization 全量复制。master 启动一个后盾线程,生成 RDB 快照,并且将新收到的写命令缓存在内存中。

master 会将 RDB 发送给 slave,slave 会先写入本地磁盘,而后再从磁盘加载到内存中,接着 master 会将内存中的缓存的写命令发送到 slave,slave 也会同步这些数据。

如果 slave 和 master 断开连接会主动重连,连贯之后 master 仅会复制给 slave 局部短少的数据。

断点续传

从 Redis2.8 开始,就反对主从复制的断点续传,如果主从复制过程中,断开了连贯,能够接着上次复制的中央持续复制上来。
master 会在内存中保护一个 backlog,master 和 slave 都会保留一个 replica offset 还有一个 master run id, offset 就是保留在 backlog 中的,如果 master 和 slave 网络连接断掉了,slave 会让 master 从上次 replica offset 开始持续复制,如果没有找到对应的 offset,那么就会执行一次 resynchronization。

无磁盘化复制

master 在内存中创立 RDB,而后发送给 slave,不会在本人本地落地磁盘了。只须要在配置文件中开启repl-diskless-sync yes

如果主机宕机了

主机宕机,群龙无首,如果本台服务器想当老大,能够执行如下命令:

redis> slaveof no one

应用整个命令让本人变成主机,其余从机还是要手动抉择它作为老大。
但如果这个时候,老大又好了,,,,老大也不是老大了,还得手动改回去

哨兵模式

哨兵模式就是主动抉择老大的模式。
Redis 从 2.8 开始正式提供了 Sentinel(哨兵)架构来解决这个问题。

哨兵模式是一种非凡的模式,首先 Redis 提供了哨兵的命令,哨兵是一个独立的过程,作为过程,它会独立运行,其原理是哨兵通过发送命令,期待 Redis 服务器响应,从而监控运行的多个 Redis 实例。

Redis 哨兵主备切换的数据失落问题

导致数据失落的两种状况

主备切换的过程,可能导致数据失落:

  • 异步复制导致的数据失落
    因为 master-slave 的复制是异步的,所以可能有局部数据还没复制到 slave,master 就宕机了,此时这部分数据就失落了
  • 脑裂导致的数据失落
    脑裂,也就是说,某个 master 所在机器忽然脱离了失常的网络,跟其余 slave 机器不能连贯,但实际上 master 还运行着。哨兵 认为 master 宕机,从新选了一个,集群中有两个 master。
    尽管某 slave 变成 master,但可能客户端还没来得及切换到新的 master,还持续向旧 master 写数据。旧 master 复原的时候,会作为 slave,本人的数据被清空,新 master 没有前面客户端写入的数据,所以这部分数据失落了。

数据失落的解决方案

进行如下配置:

min-slaves-to-write 1
min-slaves-max-lag 10

示意要求至多有 1 个 slave,数据复制和同步的提早不能超过 10 秒
一旦所有的 slave,数据复制和同步的提早都超过了 10 秒钟,master 就不会再接管任何申请了

缩小异步复制数据的失落
有了 min-slaves-max-lag 配置,就能够确保说,一旦 slave 复制数据和 ack 延时过长,就认为可能 master 宕机后损失的数据太多了,那么就回绝写申请,这样能够把 master 宕机时因为数据未同步到 slave 导致的数据失落升高的可控范畴内。

缩小脑裂的数据失落
如果一个 master 呈现了脑裂,跟其余 slave 丢了连贯,那么下面两个配置能够确保说,如果不能持续给指定数量的 slave 发送数据,而且 slave 超过 10 秒没有给本人 ack 音讯,那么就间接回绝客户端的写申请。因而在脑裂场景下,最多失落 10 秒数据。

启动 Sentinel

用户须要在配置文件中指定想要被 Sentinel 监督的主服务器,并且 Sentinel 也须要在配置文件中写入信息以记录主从服务器的状态。

$> redis-sentinel sentinel.conf 
// 配置文件门路看具体的, 也可能是 /etc/sentinel.conf

Sentinel 配置文件须要蕴含以下选项:

sentinel monitor <master-name> <ip> <port> <quorum>
  • master-name: 指定主服务器的名字
  • quorum: 判断这个主服务器下线所需的 Sentinel 数量

当 Sentinel 开始监督一个主服务器之后,就会去获取被监督主服务器的从服务器名单,并依据名单对各个从服务器施行监督,整个过程是齐全主动的,所以用户只需输出待监督主服务器的地址就能够了。

Sentinel 会对每个被监督的主从服务器施行心跳检测,记录各个服务器的在线状态、响应速度等信息,当 Sentinel 发现被监督的主服务器进入下线状态时,就会开始故障转移。

redis-sentinel 是一个运行在非凡模式下的 Redis 服务器,
也能够应用:
redis-server sentinel.conf --sentinel去启动一个 sentinel

一个 Sentinel 能够监督多个主服务器,
在配置文中指定多个 sentinel monitor 选项,
给不同的主机设置不同的名字

新主机筛选规定

设置从服务器优先级:
配置文件里的replica-priority

  • 默认值 100,值越小优先级越高。
  • 0 示意永远不会被选为主服务器。

新主机的筛选规定:

先剔除不符合条件的从服务器

  • 否决曾经下线、长时间没用回复心跳检测疑似下线的服务器
  • 否决长时间没有与主机通信,数据状态过期的服务器
  • 否决优先级为 0 的服务器

依据以下规定,在残余的当中选

  • 优先级最高的从服务器
  • 等同优先级复制偏移量最大的服务器
  • 以上都雷同选运行 ID 最小的

Sentinel 网络

只抉择一个哨兵对 Redis 服务进行监控,可能会呈现问题,能够应用多个哨兵进行监控,各个哨兵之间还能够相互监控,这样就造成了多哨兵模式。

假如主服务器宕机,哨兵 1 查看到了这个后果,但他不会马上进行 failover(故障转移) 过程,仅仅只是哨兵 1 主观的认为主服务器不可用,这个景象称为主观下线。当前面的哨兵也检测到主服务器不可用,并且数量达到肯定值时,哨兵之间就会进行一次投票,投票的后果由一个哨兵发动,进行 failover(故障转移)操作。切换胜利后,就会告诉公布订阅模式,让各个哨兵把本人监控的从服务器实现切换主机,这个过程称为主观下线。

当 Sentinel 网络中的其中一个 Sentinel 认为某个主服务器曾经下线时,它会将这个主服务器标记为主观下线,而后询问其余的 Sentinel 是否也将这个主服务器标记成了 主观下线 (sdown)
当这个批准主机下线的数量达到配置中设置的 quorum 指定的数量时,将主服务器标记为 主观下线(odown)

哨兵集群的主动发现机制

哨兵相互之间的发现,是通过 Redis 的 pub/sub 零碎实现的。
每个哨兵都会往__sentinel__:hello 这个 channel 里发送一个音讯,这时候所有其余哨兵都能够生产到这个音讯,并感知到其余哨兵的存在。

每隔两秒钟,每个哨兵都会往本人监控的某个 master+slaves 对应的__sentinel__:hello channel 里发送一个音讯,内容是本人的 host、ip、runid 还有对这个 master 的监控配置。

每个哨兵也会去监听本人监控的每个 master+slaves 对应的__sentinel__hello channel,而后去感知到同样在监听这个 master+slaves 的哨兵的存在。

每个哨兵还会跟其余哨兵替换对 master 的监控配置,相互过程监控配置的同步。

Sentinel 治理命令

获取所有被监督主服务器的信息

redis> sentinel masters

获取指定被监督主服务器的信息

redis> sentinel master <master-name>

获取被监督主服务器的从服务器信息

redis> sentinel slaves <master-name>

获取其余 Sentinel 的相干信息

redis> sentinel sentinels <master-name>

重置主服务器状态

sentinel reset <pattern>

强制执行故障转移

sentinel failover <master-name>

查看可用 Sentinel 的数量

sentinel ckquorum <master-name>

集群 ✍

Redis 集群是 Redis 3.0 版本开始正式引入的性能,带来了在线扩大 Redis 零碎读写性能的能力。

根本个性

  • 提供主从复制性能、Sentinel 性能,局部 master 不可用还是能够持续工作
  • 分片和重分片,Redis 集群会将整个数据空间划分为 16384(slot) 来实现数据分片,集群中的各个主节点会别离负责解决其中的一部分
  • 集群采纳无代理模式,客户端发送的命令会间接交给节点执行

Redis cluster 间接集成了 replication 和 sentinel 的性能,所以具备高可用性,主备替换原理也基本上是统一的。

在 Redis cluster 架构下,每个 Redis 要凋谢两个端口号,比方一个是 6379,另一个是加 1 万的端口号,16379。
16379 端口号是用来进行节点间通信的,也就是 cluster bus 的货色。
cluster bus 的通信,用来进行故障检测、配置更新、故障转移受权。cluster bus 用了另外一种二进制的协定,gossip协定,用于节点间高效数据交换。

搭建集群

搭建集群有两种办法:

  • 应用源码附带的集群主动搭建程序
  • 配置文件手动搭建集群

F1 主动搭建

create-cluster 程序位于源码的 utils/create-cluster/create-cluster 地位

通过 start 命令创立 6 个节点:

应用 create 命令把 6 个节点组合成一个集群,包含 3 个主节点和 3 个从节点


create 命令会依据现有节点制订出一个响应的角色和槽调配打算,会询问一下你的意见。
这里是 30001、30002、30003 被设置为主节点,别离负责槽 0 ~5460、5461 ~ 10922、10923 ~ 16383,30004、30005、30006 别离设置为以上 3 个节点的从节点。

胜利构建集群后,就能够应用客户端来连贯和应用集群。


敞开集群,清理节点信息:


F2 手动搭建

搭建一个 3 个主节点和 3 个从节点组成的 Redis 集群。
创立 6 个文件夹,用于寄存相应节点数据和配置文件。

redis.conf 里的内容:

cluster-enabled yes
port 30001

启动 6 个节点:

连贯 6 个节点,并为它们调配槽:

执行命令:redis-cli --cluster create 127.0.0.1:30001 127.0.0.1:30002 127.0.0.1:30003 127.0.0.1:30004 127.0.0.1:30005 127.0.0.1:30006 --cluster-replicas 1

这里留神,redis 版本要 5.0.0 以上能力应用 --cluster

这样就胜利了,打印的信息能够看到具体的分配情况。

如果想从新搭建集群,先将下图 30001~30006 的 server 的 PID 给 kill

而后再将一个个节点从新 redis-server redis.conf 启动

散列标签

能够通过散列标签将本来不属于同一个槽的键放到雷同的槽里。
(ps: 集群客户端命令须要加 -c)

应用散列标签,该性能会找出键中第一个被大括号突围并且非空的字符串子串,而后依据子串计算出该键所属的槽。即便两个键原本不属于一个槽,只有领有雷同的被突围子串,就能够把它们放到同一个槽中。

尽管从逻辑上说,把 user::101 和{user}::101 看作同一个键,但散列标签只是 Redis 集群对键名的一种非凡解释,这两个键在理论中不雷同,它们同时存在于数据库,由下面的图也能够看进去。

节点间的外部通信机制

根本通信原理

集群元数据的保护有两种形式:集中式、Goosip 协定。
Redis cluster 节点间采纳 gossip 协定进行通信,所有节点都持有一份元数据,不同的节点如果呈现了元数据的变更,就一直将元数据发送给其余节点,让其余节点进行元数据的变更。

goosip 益处在于,元数据的更新比拟扩散,不是集中在一个中央,更新申请会陆陆续续打到所有节点下来更新,升高了压力,不好的在于元数据的更新有提早。

gossip 协定

gossip 协定蕴含多种音讯,蕴含 ping、pong、meet、fail 等等

  • meet : 某个节点发送 meet 给新退出的节点,让新节点退出集群中,而后新节点就会开始与其余节点进行通信。
  • ping : 每个节点都会频繁给其余节点发送 ping,其中蕴含本人的状态还有本人保护的集群元数据,相互通过 ping 替换元数据。
  • pong : 返回 ping 和 meet,蕴含本人的状态和其余信息,也用于信息播送和更新。
  • fail : 某个节点判断另一个节点 fail 之后,就发送 fail 给其余节点,告诉其余节点某节点宕机。

分布式寻址算法

  • hash 算法 (大量缓存重建)
  • 一致性 hash 算法(主动缓存迁徙) + 虚构节点(主动负载平衡)
  • Redis cluster 的 hash slot 算法

图解一致性 hash 算法

hash 算法

给一个 key,计算 hash 值,对节点数取模,打在对应的 master 节点上。
问题是如果某台机器宕机了,取模无奈拿到想要的数据,因为节点数变了。

一致性 hash 算法

将整个 hash 值空间组织成一个虚构的圆环,整个空间按顺时针方向组织。
将各个 master 节点 (应用服务器的 ip 或主机名) 进行 hash。这样就能确定每个节点在 hash 环上的地位。

给一个 key,计算 hash 值,确定在环上的地位,从次地位沿环顺时针走,遇到的第一个 master 节点就是 key 所在的地位。

该办法如果有一个节点挂了,受影响的也仅仅只是此节点到环空间前一个节点之间的数据,其余都不受影响。减少一个节点也是这样。

然而当节点太少的时候,节点散布不平均会造成 缓存热点,所以引入了虚构节点机制,对每个节点计算多个 hash,每个计算结果都搁置一个虚构节点。这样就能够实现数据均匀分布,负载平衡。

Redis cluster 的 hash slot 算法

Redis cluster 有固定的 16384 个 hash slot,对每个 key 计算 CRC16 值,而后对 16384 取模,能够获取 key 对应的 hash slot。
Redis cluster 每个 master 都会持有局部 slot,hash slot 让节点的减少移除很简略,减少一个 master,就将其余 master 的 hash slot 挪动局部过来,缩小一个 master,就将它的 hash slot 挪动到其余 master 下来。挪动 hash slot 的老本是很低的,任何一台机器宕机对其余机器都不影响,因为 key 找的是 hash slot,不是机器。

Redis cluster 的高可用原理

  • 判断节点宕机

    和哨兵模式一样有主观宕机和主观宕机,在 cluster-node-timeout 内,某节点始终没有返回 pong,被认为 pfail(主观宕机)
    如果一个节点认为某节点宕机,会在 gossip ping 音讯中,ping 给其余节点,如果超过半数节点都认为宕机,就会判断为主观宕机、

  • 从节点过滤

    对于宕机的 master,从其所有的 slave node 中,选一个切换为 master node,如果 salve 和 master 断开连接的工夫超过了cluster-node-timeout * cluster-slave-validity-factor,那么就没有资格切换成 master。

  • 从节点选举

    每个从节点,依据本人对 master 复制数据的 offset,来设置一个选举工夫,offset 越大的从节点,选举工夫越靠前,优先进行选举。
    所有的 master 节点开始 slave 选举投票,要给进行选举的 slave 进行投票,如果大部分 master 都投票给了某个从节点,那么选举通过,那个从节点能够切换成 master。从节点执行主备切换,从节点切换为主节点。

缓存穿透和雪崩 ✍

缓存雪崩

比方某平台每天的高峰期,每秒 10000 个申请,缓存在高峰期能够抗住每秒 8000 个申请,但 是缓存产生了全盘宕机 ,所有的申请都给了数据库,数据库挂了。
这就是缓存雪崩。

缓存雪崩的解决方案:

  • 事先:Redis 高可用,主从 + 哨兵,Redis cluster,防止全盘解体
  • 事中:本地 ehcache 缓存 +hystrix 限流 & 降级,防止 MySQL 被打死
  • 预先:Redis 长久化,一旦重启,主动从磁盘上加载数据,疾速复原缓存数据

【encache】
EhCache 是一个纯 Java 的过程内缓存框架,具备疾速、精干等特点,是 Hibernate 中默认的 CacheProvider。

【Hystrix】
hystrix 是高可用性保障的一个框架,能够让咱们在分布式系统中对服务间的调用进行管制,退出一些调用提早或者依赖故障的容错机制。
Hystrix 通过将依赖服务进行资源隔离,进而阻止某个依赖服务呈现故障时在整个零碎所有的依赖服务调用中进行蔓延,同时 Hystrix 还提供故障时的 fallback 降级机制。

用户发送一个申请,零碎收到申请后,先查本地 ehcache 缓存,如果没有查到再查 Redis。如果 ehcache 都没有,再查数据库,将数据库的后果写入 ehcache 和 Redis 中。

组件限流,能够设置每秒的申请,有多少能通过组件,残余的未通过的申请能够走 降级,能够返回一些默认的值,或者情谊提醒,或者空值。

  • 数据库相对不会死,限流组件确保了每秒只有多少个申请能通过
  • 只有数据库不死,对用户来说还是有局部申请能够被解决的
  • 只有还有局部申请不死,零碎就没死,对用户来说就是多刷几次

缓存穿透

对于某零碎,一秒 6000 个申请,后果 5000 个都是歹意攻打。5000 个攻打 缓存中查不到 数据库中也查不到
每次申请都 直奔数据库,这种歹意的攻打场景的缓存穿透就会间接把数据库给打死。

解决形式:每次零碎从数据库中只有没查到,就写一个空值到缓存中,而后设置一个过期工夫,这样的话,下次有雷同的 key 来拜访的时候,在缓存生效之前,都能够间接从缓存中取数据。

缓存击穿

缓存击穿就是说某个 key 十分热点,拜访十分频繁,处于集中式高并发拜访的状况,当这个 key 在生效的霎时,大量的申请就击穿了缓存,间接申请数据库。

解决方案如下:

  • 若缓存的数据是根本不会产生更新的,则可尝试将该热点数据设置为永不过期
  • 若缓存的数据更新不频繁,且缓存刷新的整个流程耗时较少的状况下,则能够采纳基于 Redis、Zookeeper 等分布式中间件的分布式互斥锁,或者本地互斥锁以保障仅大量的申请能申请数据库并从新构建缓存,其余线程则在锁开释后能拜访到新缓存。
  • 若缓存的数据更新频繁或者在缓存刷新的流程耗时较长的状况下,能够利用定时线程在缓存过期前被动地从新构建缓存或者延后缓存的过期工夫,以保障所有的申请能始终拜访到对应的缓存。

缓存和数据库的双写一致性✍

如果零碎不是严格要求缓存数据库必须放弃一致性的话,能够 读申请和写申请串行化,串到一个内存队列里去
串行化能够保障肯定不会呈现不统一的状况,然而它也会导致系统的吞吐量大幅度降低。

Cache Aside Pattern

最经典的缓存 + 数据库读写的模式,就是 Cache Aside Pattern。

  • 读的时候先读缓存,缓存没有去数据库读,而后取出数据后放入缓存,同时返回响应。
  • 更新的时候,先更新数据库,而后再删除缓存(不是更新!因为有时候缓存不仅只是数据库中间接取出来的值,可能须要缓存的是两个数据的计算值)

缓存不统一解决方案

  • 先更新数据库,再删除缓存,如果删除缓存失败了,那么会导致数据库中是新数据,缓存中是旧数据,数据就呈现了不统一。

🔋解决方案:先删除缓存,再更新数据库。

  • 然而如果先删除了缓存,再批改数据库时一个申请过去,去读缓存,发现缓存空,去读数据库,查到了旧数据,放到了缓存中,而后前面数据库实现了更新,又不统一了。

🔋解决方案:这里我临时也无奈给出精确答案。并发场景下如果要更新数据库,能够强制删除缓存反复更新申请几次?或者更新数据的时候给数据加个标识,如果发现在缓存里找不到数据,就删除缓存从新更新数据库。(未必正确)

退出移动版