" name="sm-site-verification"/>
侧边栏壁纸
博主头像
PySuper 博主等级

千里之行,始于足下

  • 累计撰写 206 篇文章
  • 累计创建 14 个标签
  • 累计收到 2 条评论

目 录CONTENT

文章目录

Redis 高可用 -- Cluster(写操作负载均衡)

PySuper
2021-12-08 / 0 评论 / 0 点赞 / 22 阅读 / 0 字
温馨提示:
本文最后更新于2024-06-23,若内容或图片失效,请留言反馈。 所有牛逼的人都有一段苦逼的岁月。 但是你只要像SB一样去坚持,终将牛逼!!! ✊✊✊
  • redis-cluster 采用数据分片的方式将数据存储在多个节点上,突破了单机的存储限制
  • 集群中的每个节点都可以对客户端提供读写服务,相对于单机拥有更高的并发能力

集群架构

  • 采用无中心结构 + Gossip 协议,每个节点保存数据和整个集群状态,每个节点都和其他所有节点连接
  • 每个Redis节点都互相两两相连,客户端访问集群中的 任意一台,都可以对其他 Redis 节点进行 读写 操作
  • 节点的 fail 是通过集群中超过半数的节点检测失效时才生效
cluster-1.png

实现原理

主从模式

  • 一个主节点对应一个或多个从节点,主节点提供数据存取,从节点则是从主节点拉取数据备份
  • 当这个主节点挂掉后,就会有这个从节点选取一个来充当主节点,从而保证集群不会挂掉
  • 如果某个主节点和从节点,都发生故障,集群将完全处于不可用状态

数据分片

  • 一个 Redis 集群包含 16384 个哈希槽(hash slot)
  • 数据库中的每个键都属于这 16384 个哈希槽的其中一个
  • 集群使用公式 CRC16 (key) % 16384 来计算键 key 属于哪个槽
  • 集群中的每个节点负责处理一部分哈希槽
Redis 客户端定位数据所在节点.png

选主流程

跟哨兵类似,两者都是基于 Raft 算法来实现的

  • 集群的配置纪元 +1,是一个自曾计数器,初始值 0 ,每次执行故障转移都会 +1
  • 检测到主节点下线的从节点向集群广播一条CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST消息,要求所有收到这条消息、并且具有投票权的主节点向这个从节点投票
  • 这个主节点尚未投票给其他从节点,那么主节点将向要求投票的从节点返回一条CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK 消息,表示这个主节点支持从节点成为新的主节点
  • 参与选举的从节点都会接收 CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK 消息,如果收集到的票 >= (N/2) + 1 支持,那么这个从节点就被选举为新主节点
  • 如果在一个配置纪元里面没有从节点能收集到足够多的支持票,那么集群进入一个新的配置纪元,并再次进行选举,直到选出新的主节点为止
cluster-3.png

搭建 Redis-Cluster

要让集群正常工作至少需要 3 个主节点,在这里我们要创建 6 个 redis 节点,其中三个为主节点,三个为从节点

为了方便演示,这 6 个 redis 部署在同一台机器,采用不同的端口号 (7001 ~ 7006)

配置文件

6 个 Redis 节点的配置文件分别命名为:redis-7001.conf、redis-7002.conf… … redis-7006.conf

除了端口号不同外,其余配置相同,配置如下:

# 后台执行
daemonize yes

# 端口号
port 7000

# 为每一个集群节点指定一个 pid_file
pidfile ~/Desktop/redis-cluster/redis_7001.pid

# 启动集群模式
cluster-enabled yes

# 每一个集群节点都有一个配置文件,这个文件是不能手动编辑的。确保每一个集群节点的配置文件不通
cluster-config-file redis-7001.conf

# 集群节点的超时时间,单位:ms,超时后集群会认为该节点失败
cluster-node-timeout 5000

# 最后将 appendonly 改成 yes(AOF 持久化)
appendonly yes
# 启动 6 个 Redis 实例
/usr/local/redis/bin/redis-server redis_7001.conf
/usr/local/redis/bin/redis-server redis_7002.conf
/usr/local/redis/bin/redis-server redis_7003.conf
/usr/local/redis/bin/redis-server redis_7004.conf
/usr/local/redis/bin/redis-server redis_7005.conf
/usr/local/redis/bin/redis-server redis_7006.conf

使用ps -ef | grep redis查看,可以看到 6 个 Redis 实例都以 cluster 的方式启动了

cluster-4.png

实例启动后还处于各自独立的状态,还没有形成集群,需要手动执行命令建立集群

建立集群

执行命令:

# redis-cli --cluster create --cluster-replicas 1 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 127.0.0.1:7006

redis-cli --cluster create --cluster-replicas 1 \
	127.0.0.1:7001 \
	127.0.0.1:7002 \
	127.0.0.1:7003 \
	127.0.0.1:7004 \
	127.0.0.1:7005 \
	127.0.0.1:7006

--replicas 1 的意思是:我们希望为集群中的每个主节点创建一个从节点

cluster-5.png
看到 `[OK]` 的信息之后,就表示集群已经搭建成功了,可以看到,这里我们正确地创建了三主三从的集群

验证集群

# 使用 redic-cli 任意连接一个节点
redis-cli -c -h 127.0.0.1 -p 7001
  • -c:表示集群模式
  • -h:指定 ip 地址
  • -p:指定端口
# 查看节点列表
cluster nodes

# 可以看出
7001 -- 7006
7002 -- 7004
7003 -- 7005
cluster-6.png
127.0.0.1:7001> SET name zheng
-> Redirected to slot [5798] located at 127.0.0.1:7002
OK
  • Redis 自动帮我们进行了 Redirected 操作跳转到了 7002 这个实例上
  • 我们可以在任意节点使用 cluster nodes 查看节点列表

故障模拟

cluster-7.png
  • 从上图我们可以看到 7006 是7001 的从节点,现在停掉 7001(``kill -9`)
    • 7006升为主节点,集群依然可用
    • 数据查询正常
  • 再把升为主节点的7006停掉
    • 显示(error) CLUSTERDOWN The cluster is down
    • 集群不再可用

一致性保证

Redis 集群不保证强一致性

实践中,这意味着在特定的条件下,Redis 集群可能会丢掉一些被系统收到的写入请求命令

Redis 集群为什么会丢失写请求?

  • 采用了异步复制,这意味着在写期间下面的事情发生了:
    • 你的客户端向主服务器 B 写入
    • 主服务器 B 回复 OK 给你的客户端
    • 主服务器 B 传播写入操作到其从服务器 B1,B2 和 B3

注意事项

  • 如果想重新创建集群,需要登录到每个节点,执行 flushdb,然后执行 cluster reset,重启节点
  • 如果要批量杀掉 Redis 进程,可以使用 pkill redis-server 命令
  • Redis 开启密码认证后,在集群操作时问题会比较多,因此建议不要开启密码认证,搭配使用防火墙保证 Redis 的安全
  • 如果 redis 开启了密码认证:
    • 需要在 redis.conf 中增加属性:masterauth yourpassword
    • 并且需要修改 /usr/local/share/gems/gems/redis-3.3.3/lib/redis 目录下的 client.rb 文件
    • 将 password 属性设置为 redis.conf 中的 requirepass 的值:
      • 不同的操作系统 client.rb 的位置可能不一样
      • 可以使用 find / -name “client.rb” 全盘查找一下
 DEFAULTS = {
      :url => lambda { ENV["REDIS_URL"] },
      :scheme => "redis",
      :host => "127.0.0.1",
      :port => 6379,
      :path => nil,
      :timeout => 5.0,
      :password => "yourpassword",
      :db => 0,
      :driver => nil,
      :id => nil,
      :tcp_keepalive => 0,
      :reconnect_attempts => 1,
      :inherit_socket => false
    }


0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin

评论区