redis主从、哨兵、集群

redis主从、哨兵、集群

redis实际生产过程中可能出现的问题:

  1. 机器宕机,redis直接停用,影响业务
  2. 容量瓶颈,内存大小不够
  3. QPS瓶颈,读的并发量很高很高,一个服务不够

redis主从(master-slave)

主从实现的功能:

  1. 做读写分离:主库用来写,从库只用来读
  2. 做数据副本
  3. 机器故障时,可以让一个从库变成主库,业务不会收到影响

我们可以做一主一从,也可以做一主多从,一个master可以拥有多个slave,但是一个slave只能有一个master。

redis主从赋值流程:

  1. 副本(从)库通过slaveof 127.0.0.1 6379命令,连接主库,并发送SYNC给主库
  2. 主库收到SYNC,会立即触发BGSAVE,后台保存RDB,发送给副本库
  3. 副本库接收后会应用RDB快照,load进内存
  4. 主库会陆续将中间产生的新的操作,保存并发送给副本库
  5. 到此,我们主复制集就正常工作了
  6. 再此以后,主库只要发生新的操作,都会以命令传播的形式自动发送给副本库.
  7. 所有复制相关信息,从info信息中都可以查到.即使重启任何节点,他的主从关系依然都在.
  8. 如果发生主从关系断开时,从库数据没有任何损坏,在下次重连之后,从库发送PSYNC给主库
  9. 主库只会将从库缺失部分的数据同步给从库应用,达到快速恢复主从的目的

前三步是从库初始化去复制主库,中间是当主库发送变化时,从库跟进更新,最后描述了主库和从库在关闭重启后如何保持同步和主从关系。

redis配置主从

# 命令形式
slaveof 127.0.0.1 6379  # slaveof ip port  # 需要从库初始是空的
slaveof no one  # 断开主从关系,不在同步
# 配置文件添加
slaveof 127.0.0.1 6379  # 配置从节点ip和端口
slave-read-only yes  # 从节点只读,因为可读可写,数据会乱
min-slaves-to-write 1  # 从服务器至少有一个才能执行写
min-slaves-max-lag 3  # 如果有从库延迟了3秒以上,主服务器拒绝写入

验证redis的主从

  • 连接redis从库,尝试写入数据,应该被拒绝并提示只读
  • 连接redis主库,写入数据,再连接从库取出数据
  • 执行info Replication查看主从的相关参数

如果主从没能正常连接,要注意可能是防火墙的端口没开,使用iptables系列指令去管理一下开放的端口,摆烂式开放端口iptables -F

哨兵Sentinel

上述建立主从的过程,需要人为的去操作,有这么一种场景,如果主库redis服务挂了,那么我们就需要去将从库变为主库,那么操作的这段时间内,写的业务是就处于停滞状态。

于是,这个主从切换的过程,我们可以用自动化的方式实现,可以用哨兵。哨兵会监控当前的库,监控的主库出了故障,就会做主节点转移,将一个从库设置为主库。

哨兵Sentinel也是实现高可用的必备,高可用是指服务的可用性很高,业务上几乎可以一直访问,不会出现故障很久影响很大的情况。

工作流程原理:

  1. 多个sentinel监控master服务节点,这个master有几个slave
  2. 多个sentinel发现并确认master有问题(宕机等)
  3. 选举出一个sentinel作为领导
  4. 选出一个slave作为新的master
  5. 通知其余slave成为新master的slave
  6. 通知客户端的主从变化(一些写的服务会切换到新master)
  7. 老的master如果重启,则会被管理成为新master的slave

n个sentinel哨兵会管理redis的节点关系,哨兵会根据主节点找到其从节点,主节点故障时做主从切换,而暂时失效的节点(无论主从)复活时,都会被重新连接,作为现在master的slave。

部署哨兵

部署哨兵前,先按照上一步部署主从。那这次我选择在三台机器上部署三个redis。

  1. 选择已经安装好redis的虚拟机,克隆两台出来
  2. 第一台原型机的redis服务使用配置为主,按照正常的redis配置即可,port为6379,而这台机器的ip为10.0.0.4(这是我的虚拟机网络配置)
  3. 两台克隆机的redis服务配置为从,需要加入slaveof和slave-read-only的配置,将10.0.0.4的redis服务作为主服务。

然后我们再去配置哨兵:

  1. 哨兵配置文件

    port 26379
    daemonize yes
    dir /root/redis/data/
    bind 0.0.0.0
    logfile "redis_sentinel.log"
    sentinel monitor mymaster 10.0.0.4 6379 2
    sentinel down-after-milliseconds mymaster 30000
    # 端口启动在26379
    # 后台启动
    # 工作目录
    # 绑定0.0.0.0,外部也能访问到
    # 日志文件名
    # sentinel monitor <master-name> <master-ip> <port> <n> 
    # 哨兵监控的主节点,mymaster是起的名字,多个哨兵监听的名字一致,ip port是主节点的ip端口,
    # 2是指几个哨兵认为master有问题就有问题,一般3个哨兵我们就写两个,4个就写三个,大概写一半+1即可,多数认为有问题,哨兵则做主从切换
    # 哨兵监控的主节点有多少微秒连接不上,我们就认为有问题,这里是30秒
    
  2. 因为每台机器上都有redis,所以我们每台机器上的哨兵配置文件都像上面配置即可。

  3. 在每一台机器上启动一个哨兵(并不是一台只能启动一个,可以启动多个哨兵服务,但是这样不能采取同样的端口配置了)

    redis-sentinel sentinel.conf    # 按照配置文件启动哨兵服务
    
  4. 每台机器上可以使用ps aux |grep redis来查看自己的redis相关服务,按照以上步骤,应该每台机器都会启动一个redis-sentinel服务跑在26379端口

  5. 测试哨兵是否起到了主从自动切换的功能,连接主库的redis服务,shutdown关闭主库redis服务。

    哨兵因为配置了30秒的时间去确认master是否真的挂了,此时如果快速的去重启这个10.0.0.4的服务,那么会发现,主库还是主库

    但如果等待了30秒后,主库没有重启,那么会发现有一个从库变成了主库,使用info指令可以看到,也可以用写入指令来验证。

    当有新的master产生后,我们老的master重新连接,则变成从库,也可以info验证。

redis集群

主从和哨兵替我们解决了读写分离和高可用的需求,提高了一些性能,但是如果想更好的提高性能(并发量和数据内存的瓶颈),我们需要加机器来解决,那么redis集群可以为我们提供分布式方案部署redis服务。

多机部署的问题是,怎么将数据分布到多台机器后,我还能快速准确的定位到我想要的数据将其取出。

有两个主流的分区方式,我们称之为数据分片的方式:

  • 哈希分布
  • 顺序分布

顺序分布式将数据直接切开一样放到多台机器,一般关系型数据库集群会采取这种分区方式,这种方式扩充容量比较方便。

而哈希分布也有两种:节点取余分布,大概过程时我们通过hash算法将key转换为某个值,这个值除以一个值,得到余数以分配到不同节点上,是hash+取余。一致性哈希分区,哈希+顺时针(优化取余)。

redis的策略是:

  • 对key进行hash得到数字对16383取余
  • 余数对应槽,每个节点被分配了不同的槽
  • 这样的策略,通过key可以找到数据所在的节点,存取都按这套规则

集群搭建

一些概念

  • 节点:某一台机器
  • meet:节点跟节点之间通过meet通信
  • 指派槽:16384个槽分给几个节点
  • 复制:主从复制
  • 高可用:主节点过掉,从节点顶上

搭建步骤:

  1. 配置文件,这里采取两主两从的集群方案(一个从只能跟一个主,但是一个主可以配多个从,所以也可以做两主四从)

    # 集群相关配置
    cluster-enabled yes
    cluster-node-timeout 15000
    cluster-config-file node.conf
    cluster-require-full-coverage yes
    # 这些配置还是放在redis.conf中,节点本身的配置还是需要配的,
    # 如工作目录,日志文件等,但是由于布置在多态机器上,所以不需要做文件名字的划分
    
  2. 将上述redis.conf配置在4个节点(4台机器相应的位置)

  3. 正常启动4个节点的redis服务

  4. 基于已创建的redis服务创造集群

    # 一条指令搭建集群(做了meet、复制、主从三大过程)
    redis-cli --cluster create --cluster-replicas 1 10.0.0.4:6379 10.0.0.5:6379 10.0.0.6:6379 10.0.0.7:6379
    # 其中1是一主有1从,如我配置了6个节点,这里的数字为2,则会分配2主4从
    

    ps:这个过程可以分开多个指令去自定义的做这个过程,但相对来步骤繁杂

  5. 通过info、nodes、slots指令查看相关信息

    • info:查看redis服务所有相关信息,可以指定section看某一段
    • nodes:查看redis集群的节点信息
    • slots:查看每个节点指派槽的信息
  6. 存值试试,查看各节点的变化,注意,创建集群后,redis-cli无论连接哪个节点,所进行的读写操作将都是基于集群模式的。

    也就是都可以写,写完,会自动分配到该分配的槽的节点,任意位置也都能取到之前存放的值。

  7. 主从切换测试,挂掉一个主节点,看它会怎么做主从切换。

集群扩容(了解)

有时服务量变大,原本的集群已经承受不了了,那么就需要扩容,相应的,当服务量变小,也需要做缩容。其原理是重新指派了槽,所以集群间的数据要做迁移,为了这种迁移的影响变小,最好是成倍扩容(当然了成本也很高)

以下,是在原有集群基础上,添加两个节点(一主一从)的流程

  1. 准备两台机器并启动其中的redis服务

  2. 两台机器加入到集群中

    redis-cli --cluster add-node 10.0.0.8:6379 10.0.0.4:6379
    redis-cli --cluster add-node 10.0.0.9:6379 10.0.0.4:6379
    
  3. 让10.0.0.9复制10.0.0.8

    redis-cli -h 10.0.0.9 cluster replicate
    
  4. 迁移槽

    redis-cli --cluster reshard 10.0.0.4:6379
    

集群缩容(了解)

  1. 先下线迁移槽
  2. 下线节点,将节点关闭后,删除节点

放个链接在这里(有空再看):https://www.cnblogs.com/liuqingzheng/articles/17324393.html

python操作哨兵

使用了redis主从和哨兵后,我们连接客户端的方式就发生了变化,应该是一个动态的连接,如果主库挂掉,可以切换到新的主库连接上继续写,读最好也不用我们区分。

import redis
from redis.sentinel import Sentinel
# 哨兵的地址和端口
sentinel = Sentinel([
    ('10.0.0.4', 26379),
    ('10.0.0.5', 26379),
    ('10.0.0.6', 26379)
],socket_timeout=5)

# 获取主服务器地址
master_addr = sentinel.discover_master('mymaster')  # 这个和哨兵中配置的master-name一致

# 获取从服务器地址
slave_addr = sentinel.discover_slaves('mymaster')

python利用哨兵读写分离

master = sentinel.master_for('mymaster', socket_timeout=0.5)
w_ret = master.set('key','value')

slave  = sentinel.slave_for('mymaster', socket_timeout=0.5)
r_ret = slave.get('key')

python操作集群

搭建集群后,客户端的存取操作也要相应的变成集群模式,python中需要下载rediscluster模块

pip install redis-py-cluster

操作:

from rediscluster import RedisCluster
startup_nodes = [{"host":"10.0.0.4", "port": "6379"},{"host":"10.0.0.5", "port": "6379"},{"host":"10.0.0.6", "port": "6379"},{"host":"10.0.0.7", "port": "6379"}]
# startup_nodes是将所有的集群节点都录入
# rc = RedisCluster(startup_nodes=startup_nodes,decode_responses=True)
rc = RedisCluster(startup_nodes=startup_nodes)
rc.set("key", "bar")
print(rc.get("key"))
posted @ 2023-04-24 20:21  leethon  阅读(45)  评论(0编辑  收藏  举报