Reids分布式

1 为什么需要Redis集群

1.1 为什么需要集群?

1.1.1 性能

Redis 本身的 QPS 已经很高了,但是如果在一些并发量非常高的情况下,性能还是
会受到影响。这个时候我们希望有更多的 Redis 服务来完成工作。

1.1.2 扩展

第二个是出于存储的考虑。因为 Redis 所有的数据都放在内存中,如果数据量大,
很容易受到硬件的限制。升级硬件收效和成本比太低,所以我们需要有一种横向扩展的
方法。

1.1.3 可用性

第三个是可用性和安全的问题。如果只有一个 Redis 服务,一旦服务宕机,那么所有的客户端都无法访问,会对业务造成很大的影响。另一个,如果硬件发生故障,而单机的数据无法恢复的话,带来的影响也是灾难性的。

可用性、数据安全、性能都可以通过搭建多个 Reids 服务实现。其中有一个是主节
点(master),可以有多个从节点(slave)。主从之间通过数据同步,存储完全相同的数
据。如果主节点发生故障,则把某个从节点改成主节点,访问新的主节点。

2 Redis主从复制(replication)

2.1 主从复制配置

例如一主多从,200 是主节点,在每个 slave 节点的 redis.conf 配置文件增加一行

# slaveof 192.168.8.203 6379

配置修改: redis 5.0.5
# Generated by CONFIG REWRITE
replicaof 192.168.8.203 6379

或者在启动服务时通过参数指定 master 节点:

./redis-server --slaveof 192.168.8.203 637

启动后,查看集群状态:

redis> info replication

从节点不能写入数据(只读),只能从 master 节点同步数据。get 成功,set 失败。

127.0.0.1:6379> set gupao 666
(error) READONLY You can't write against a read only replica.

主节点写入后,slave 会自动从 master 同步数据。

断开复制:

redis> slaveof no one

此时从节点会变成自己的主节点,不再复制数据。

2.2 主从复制

2.2.1 连接阶段

1、slave node 启动时(执行 slaveof 命令),会在自己本地保存 master node 的
信息,包括 master node 的 host 和 ip。

2、slave node 内部有个定时任务 replicationCron(源码 replication.c),每隔 1
秒钟检查是否有新的 master node 要连接和复制,如果发现,就跟 master node 建立
socket 网络连接,如果连接成功,从节点为该 socket 建立一个专门处理复制工作的文件
事件处理器,负责后续的复制工作,如接收 RDB 文件、接收命令传播等。

当从节点变成了主节点的一个客户端之后,会给主节点发送 ping 请求。

2.2.2 数据同步阶段

3、master node 第一次执行全量复制,通过 bgsave 命令在本地生成一份 RDB 快
照,将 RDB 快照文件发给 slave node(如果超时会重连,可以调大 repl-timeout 的值)。
slave node 首先清除自己的旧数据,然后用 RDB 文件加载数据。

2.2.3 命令传播阶段

4、master node 持续将写命令,异步复制给 slave node

延迟是不可避免的,只能通过优化网络。

repl-disable-tcp-nodelay no

当设置为 yes 时,TCP 会对包进行合并从而减少带宽,但是发送的频率会降低,从
节点数据延迟增加,一致性变差;具体发送频率与 Linux 内核的配置有关,默认配置为
40ms。当设置为 no 时,TCP 会立马将主节点的数据发送给从节点,带宽增加但延迟变
小。

一般来说,只有当应用对 Redis 数据不一致的容忍度较高,且主从节点之间网络状
况不好时,才会设置为 yes;多数情况使用默认值 no。

2.3 主从复制的

主从模式解决了数据备份和性能(通过读写分离)的问题,但是还是存在一些不足:

1、RDB 文件过大的情况下,同步非常耗时。

2、在一主一从或者一主多从的情况下,如果主服务器挂了,对外提供的服务就不可
用了,单点问题没有得到解决。如果每次都是手动把之前的从服务器切换成主服务器,
这个比较费时费力,还会造成一定时间的服务不可用。

3 可用性保证之Sentinel

3.1 Sentinel原理

如何实现主从的自动切换?我们的思路:

创建一台监控服务器来监控所有 Redis 服务节点的状态,比如,master 节点超过一
定时间没有给监控服务器发送心跳报文,就把 master 标记为下线,然后把某一个 slave
变成 master。应用每一次都是从这个监控服务器拿到 master 的地址。

问题是:如果监控服务器本身出问题了怎么办?那我们就拿不到 master 的地址了,
应用也没有办法访问。

那我们再创建一个监控服务器,来监控监控服务器……似乎陷入死循环了,这个问题
怎么解决?这个问题先放着。

Redis 的 Sentinel 就是这种思路:通过运行监控服务器来保证服务的可用性。

从 Redis2.8 版本起,提供了一个稳定版本的 Sentinel(哨兵),用来解决高可用的
问题。它是一个特殊状态的 redis 实例。

我们会启动一个或者多个 Sentinel 的服务(通过 src/redis-sentinel),它本质上只
是一个运行在特殊模式之下的 Redis,Sentinel 通过 info 命令得到被监听 Redis 机器的
master,slave 等信息。

image

为了保证监控服务器的可用性,我们会对 Sentinel 做集群的部署。Sentinel 既监控
所有的 Redis 服务,Sentinel 之间也相互监控。

注意:Sentinel 本身没有主从之分,只有 Redis 服务节点有主从之分。

3.1.1 服务下线

Sentinel 默认以每秒钟 1 次的频率向 Redis 服务节点发送 PING 命令。如果在
down-after-milliseconds 内都没有收到有效回复,Sentinel 会将该服务器标记为下线
(主观下线)。

# sentinel.conf
sentinel down-after-milliseconds <master-name> <milliseconds>

这个时候 Sentinel 节点会继续询问其他的 Sentinel 节点,确认这个节点是否下线,
如果多数 Sentinel 节点都认为 master 下线,master 才真正确认被下线(客观下线),
这个时候就需要重新选举 master。

3.1.2 故障转移

如果 master 被标记为下线,就会开始故障转移流程。

既然有这么多的 Sentinel 节点,由谁来做故障转移的事情呢?

故障转移流程的第一步就是在 Sentinel 集群选择一个 Leader,由 Leader 完成故障
转移流程。Sentinle 通过 Raft 算法,实现 Sentinel 选举。

总结:

Sentinle 的 Raft 算法和 Raft 论文略有不同。

1、master 客观下线触发选举,而不是过了 election timeout 时间开始选举。

2、Leader 并不会把自己成为 Leader 的消息发给其他 Sentinel。其他 Sentinel 等
待 Leader 从 slave 选出 master 后,检测到新的 master 正常工作后,就会去掉客观下
线的标识,从而不需要进入故障转移流程。

3.3 Sentinel 实战

3.3.1 Sentinel 配置

为了保证 Sentinel 的高可用,Sentinel 也需要做集群部署,集群中至少需要三个
Sentinel 实例(推荐奇数个,防止脑裂)。

配置修改:

daemonize yes
port 26379
protected-mode no
dir "/usr/local/soft/redis-5.0.5/sentinel-tmp"
sentinel monitor redis-master 192.168.3.201 6379 2
sentinel down-after-milliseconds redis-master 30000
sentinel failover-timeout redis-master 180000
sentinel parallel-syncs redis-master 1

3.4 哨兵机制的不足

主从切换的过程中会丢失数据,因为只有一个 master。

只能单点写,没有解决水平扩容的问题。

如果数据量非常大,这个时候我们需要多个 master-slave 的 group,把数据分布到
不同的 group 中。

4 Redis 分布式方案

4.1 客户端 Sharding

graph LR
A[client]-->B[redis Group1]
A-->C[redis Group2]
A-->D[redis Group3]

Jedis 客户端提供了 Redis Sharding 的方案,并且支持连接池。

使用 ShardedJedis 之类的客户端分片代码的优势是配置简单,不依赖于其他中间
件,分区的逻辑可以自定义,比较灵活。但是基于客户端的方案,不能实现动态的服务
增减,每个客户端需要自行维护分片策略,存在重复代码。

第二种思路就是把分片的代码抽取出来,做成一个公共服务,所有的客户端都连接
到这个代理层。由代理层来实现请求和转发。

4.2 代理 Proxy

graph LR
A[client]-->F[Proxy]

F-->B[redis Group1]
F-->C[redis Group2]
F-->D[redis Group3]

典型的代理分区方案有 Twitter 开源的 Twemproxy 和国内的豌豆荚开源的 Codis。

4.2.1 Twemproxy

two-em-proxy的gitHub地址

graph LR
A[client]-->F[lvs]
F-->N[twem 1]
F-->M[twem 2]
N-->B[redis Group1]
N-->C[redis Group2]
N-->D[redis Group3]
M-->B[redis Group1]
M-->C[redis Group2]
M-->D[redis Group3]

Twemproxy 的优点:比较稳定,可用性高。

不足:

1、出现故障不能自动转移,架构复杂,需要借助其他组件(LVS/HAProxy +
Keepalived)实现 HA

2、扩缩容需要修改配置,不能实现平滑地扩缩容(需要重新分布数据)。

4.2.2 Codis

Codis 是一个代理中间件,用 Go 语言开发的。

功能:客户端连接 Codis 跟连接 Redis 没有区别。

image

分片原理:Codis 把所有的 key 分成了 N 个槽(例如 1024),每个槽对应一个分组,
一个分组对应于一个或者一组 Redis 实例。Codis 对 key 进行 CRC32 运算,得到一个
32 位的数字,然后模以 N(槽的个数),得到余数,这个就是 key 对应的槽,槽后面就
是 Redis 的实例。

image

Codis 的槽位映射关系是保存在 Proxy 中的,如果要解决单点的问题,Codis 也要
做集群部署,多个 Codis 节点怎么同步槽和实例的关系呢?需要运行一个 Zookeeper (或者 etcd/本地文件)。

在新增节点的时候,可以为节点指定特定的槽位。Codis 也提供了自动均衡策略。

Codis 不支持事务,其他的一些命令也不支持。

获取数据原理(mget):在 Redis 中的各个实例里获取到符合的 key,然后再汇总
到 Codis 中。

Codis 是第三方提供的分布式解决方案,在官方的集群功能稳定之前,Codis 也得到
了大量的应用。

4.3 Rsedis Cluster

Redis Cluster 是在 Redis 3.0 的版本正式推出的,用来解决分布式的需求,同时也
可以实现高可用。跟 Codis 不一样,它是去中心化的,客户端可以连接到任意一个可用
节点。

数据分片有几个关键的问题需要解决:

1、数据怎么相对均匀地分片

2、客户端怎么访问到相应的节点和数据

3、重新分片的过程,怎么保证正常服务

4.3.1 架构

Redis Cluster 可以看成是由多个 Redis 实例组成的数据集合。客户端不需要关注数
据的子集到底存储在哪个节点,只需要关注这个集合整体。

以 3 主 3 从为例,节点之间两两交互,共享数据分片、节点状态等信息。

image

4.3.2 搭建

见【redis分布式cluster搭建】文章

4.3.3 数据分布

如果是希望数据分布相对均匀的话,我们首先可以考虑哈希后取模。

一致性哈希的原理:

把所有的哈希值空间组织成一个虚拟的圆环(哈希环),整个空间按顺时针方向组
织。因为是环形空间,0 和 2^32-1 是重叠的。

Redis 虚拟槽分区

Redis 既没有用哈希取模,也没有用一致性哈希,而是用虚拟槽来实现的。

Redis 创建了 16384 个槽(slot),每个节点负责一定区间的 slot。比如 Node1 负
责 0-5460,Node2 负责 5461-10922,Node3 负责 10923-16383。

怎么让相关的数据落到同一个节点上?

在 key 里面加入{hash tag}即可。Redis 在计算槽编号的时候只会获取{}之间的字符
串进行槽编号计算,这样由于上面两个不同的键,{}里面的字符串是相同的,因此他们可
以被计算出相同的槽。

127.0.0.1:7293> set a{qs}a 1
OK
127.0.0.1:7293> set a{qs}b 1
OK
127.0.0.1:7293> set a{qs}c 1
OK
4.3.4 客户端 重定向

客户端连接到哪一台服务器?访问的数据不在当前节点上,怎么办?

服务端返回 MOVED,也就是根据 key 计算出来的 slot 不归 7191 端口管理,而是
归 7293 端口管理,服务端返回 MOVED 告诉客户端去 7293 端口操作。

这个时候更换端口,用 redis-cli –p 7293 操作,才会返回 OK。或者用./redis-cli -c
-p port 的命令(c 代表 cluster)。这样客户端需要连接两次。

Jedis 等客户端会在本地维护一份 slot——node 的映射关系,大部分时候不需要重
定向,所以叫做 smart jedis(需要客户端支持)。

4.3.5 数据迁移

新增或下线了 Master 节点,数据怎么迁移(重新分配)?

因为 key 和 slot 的关系是永远不会变的,当新增了节点的时候,需要把原有的 slot
分配给新的节点负责,并且把相关的数据迁移过来。

添加新节点(新增一个 7297):

redis-cli --cluster add-node 127.0.0.1:7291 127.0.0.1:7297

新增的节点没有哈希槽,不能分布数据,在原来的任意一个节点上执行:

redis-cli --cluster reshard 127.0.0.1:7291
4.3.6 高可用 和主 从 切换原理

当 slave 发现自己的 master 变为 FAIL 状态时,便尝试进行 Failover,以期成为新
的master。由于挂掉的master可能会有多个slave,从而存在多个slave竞争成为master
节点的过程, 其过程如下:

1.slave 发现自己的 master 变为 FAIL

2.将自己记录的集群 currentEpoch 加 1,并广播 FAILOVER_AUTH_REQUEST 信息

3.其他节点收到该信息,只有 master 响应,判断请求者的合法性,并发送
FAILOVER_AUTH_ACK,对每一个 epoch 只发送一次 ack

4.尝试 failover 的 slave 收集 FAILOVER_AUTH_ACK

5.超过半数后变成新 Master

6.广播 Pong 通知其他集群节点

Redis Cluster 既能够实现主从的角色分配,又能够实现主从切换,相当于集成了
Replication 和 Sentinal 的功能。

4.3.7 总结 :

优势

  1. 无中心架构。
  2. 数据按照 slot 存储分布在多个节点,节点间数据共享,可动态调整数据分布。
  3. 可扩展性,可线性扩展到 1000 个节点(官方推荐不超过 1000 个),节点可动
    态添加或删除。
  4. 高可用性,部分节点不可用时,集群仍可用。通过增加 Slave 做 standby 数据副
    本,能够实现故障自动 failover,节点之间通过 gossip 协议交换状态信息,用投票机制
    完成 Slave 到 Master 的角色提升。
  5. 降低运维成本,提高系统的扩展性和可用性。

不足

  1. Client 实现复杂,驱动要求实现 Smart Client,缓存 slots mapping 信息并及时
    更新,提高了开发难度,客户端的不成熟影响业务的稳定性。
  2. 节点会因为某些原因发生阻塞(阻塞时间大于 clutser-node-timeout),被判断
    下线,这种 failover 是没有必要的。
  3. 数据通过异步复制,不保证数据的强一致性。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
ava实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),可运行高分资源 Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值