主从集群

在搭建主从集群前,我们先把Redis安装起来:

#解压Redis压缩包
[root@master lf]# tar -zxvf redis-6.2.1.tar.gz
……
#安装gcc
[root@master redis-6.2.1]# yum install gcc
……
[root@master lf]# cd redis-6.2.1
#进入解压好的Redis目录,进行编译和安装
[root@master redis-6.2.1]# make
……

  

之后,我们在Redis目录下创建config和data目录,config目录用于存放Redis的配置文件,data用于存放Redis数据:

[root@master redis-6.2.1]# mkdir config
[root@master redis-6.2.1]# mkdir data
[root@master redis-6.2.1]# cp redis.conf config/
[root@master redis-6.2.1]# cd config/
[root@master config]# mv redis.conf redis-6379.conf

  

修改redis-6379.conf文件:

#默认端口号为6379,
port 6379
#关闭保护模式,开启的话,只有本机才可以访问redis
protected-mode no
#redis默认不是以守护进程的方式运行,可以通过该配置项修改,使用yes时,启用守护进程
daemonize yes
#redis以守护进程方式运行时,系统默认会把pid写入/var/run/redis.pid,可以通过pidfile指定pid文件
pidfile /var/run/redis-6379.pid
#指定日志文件名
logfile "6379.log"
#工作目录,日志、db和aof文件会存放在指定的工作目录下
dir /home/lf/redis-6.2.1/data
#设置Redis密码
requirepass 123456
#bind绑定的是自己机器网卡的ip,如果有多块网卡可以配多个ip,代表允许客户端通过机器的哪些网卡ip去访问,内网一般可以不配置bind,注释掉即可
#bind 127.0.0.1 -::1
#指定存储数据的文件名
dbfilename dump-6379.rdb

    

然后我们回到Redis目录,并根据config下redis-6379.conf配置,启动一个Redis进程。之后我们启动一个Redis客户端,在校验密码后,再在Redis上设置一个<hello,world>的键值对:

[root@master redis-6.2.1]# src/redis-server config/redis-6379.conf
[root@master redis-6.2.1]# src/redis-cli
127.0.0.1:6379> AUTH 123456
OK
127.0.0.1:6379> SET hello world
OK
127.0.0.1:6379> GET hello
"world"

  

Redis可以正常的存储数据,代表我们第一个Redis进程启动成功,6379端口的Redis进程,即为我们的主节点。下面,我们要启动两个从节点,我们向Redis的主节点写数据,主节点会向Redis从节点同步数据,我们可以向从节点读数据,从而减轻Redis主节点的读压力。

我们重新拷贝一份原先的redis配置,并命名为redis-6380.conf:  

[root@master config]# cp redis-6379.conf redis-6380.conf

  

之后,我们对redis-6380.conf做如下修改:

port 6380
protected-mode no
daemonize yes
pidfile /var/run/redis-6380.pid
requirepass 123456
logfile "6380.log"
dir /home/lf/redis-6.2.1/data
dbfilename dump-6380.rdb
#如果主节点有设置密码,从节点也需要配置主节点的校验密码
masterauth 123456
#从本机6379端口的Redis进程同步数据,Redis 5.0之前使用slaveof
replicaof 192.168.6.86 6379
#配置从节点为只读
replica-read-only yes

    

修改完毕后,我们根据redis-6380.conf重新拷贝一份redis-6381.conf,并将原先配置为6380的地方改为6381,然后我们根据这两个配置再次启动两个端口号分别为6380和6381的从节点:

[root@master config]# cp redis-6380.conf redis-6381.conf
……
[root@master redis-6.2.1]# src/redis-server config/redis-6380.conf
[root@master redis-6.2.1]# src/redis-server config/redis-6381.conf

  

然后我们查看Redis进程,可以看到目前启动了3个Redis进程:

[root@master redis-6.2.1]# ps -ef | grep redis
root 119821 1 0 Apr06 ? 00:08:30 src/redis-server *:6379
root 123856 1 0 Apr06 ? 00:07:25 src/redis-server *:6380
root 123862 1 0 Apr06 ? 00:07:41 src/redis-server *:6381

  

启动从节点后,我们在主节点里面查看从节点信息,可以看到6379服务当前的角色为主节点(master),并且看到两个从节点(slave0和slave1)的信息:

127.0.0.1:6379> INFO
……
# Replication
role:master
connected_slaves:2
slave0:ip=192.168.6.86,port=6380,state=online,offset=576,lag=1
slave1:ip=192.168.6.86,port=6381,state=online,offset=576,lag=0
master_failover_state:no-failover
master_replid:7ee936b581b51f4b389b68cc3dcfcb3737468d5c
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:576
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:576 # CPU
used_cpu_sys:0.956209
used_cpu_user:0.681712
used_cpu_sys_children:0.001589
used_cpu_user_children:0.000000
used_cpu_sys_main_thread:0.949240
used_cpu_user_main_thread:0.670907
……

我们启动一个Redis客户端连接到从节点,测试从节点是否有同步主节点的数据,并尝试在从节点进行写入操作:

[root@master redis-6.2.1]# src/redis-cli -p 6380
127.0.0.1:6380> AUTH 123456
OK
#从节点将把主节点的数据同步过来。
127.0.0.1:6380> KEYS *
1) "hello"
127.0.0.1:6380> GET hello
"world"
#尝试设置<hello,spring>的键值对,从节点报错:不能对只读副本进行写入操作。
127.0.0.1:6380> SET hello spring
(error) READONLY You can't write against a read only replica.

  

于是,我们尝试在主节点设置<hello,tomcat>键值对,并在从节点获取hello对应的value,可以看到主节点设置完毕后,依旧会将最新的值同步到从节点:

127.0.0.1:6379> SET hello tomcat
OK
127.0.0.1:6380> GET hello
"tomcat"

  

至此,主从集群算是搭建完毕,由于笔者服务器有限,所以主节点和从节点都在一台机器上,大家如果有多余的服务器,也可以在多台服务器上搭建主从集群。

Redis主从工作原理

如果你为主节点(master)配置了一个从节点(slave),不管这个从节点是否是第一次连接上主节点,它都会发送一个PSYNC命令给主节点请求复制数据。

主节点收到PSYNC命令后,会在后台进行数据持久化通过BGSAVE生成最新的rdb快照文件,持久化期间,主节点会继续接收客户端的请求,它会把这些可能修改数据集的请求缓存在内存中。当持久化进行完毕以后,主节点会把这份rdb文件数据集发送给从节点,从节点会把接收到的数据进行持久化生成rdb再加载到内存中。之后,主节点再将之前缓存在内存中的命令发送给从节点。

当主节点与从节点之间的连接由于某些原因而断开厚,从节点重新连上主节点,如果主节点收到了多个从节点并发连接请求,它只会进行一次持久化,而不是一个连接持久化一份rdb文件再单独把每个连接持久化下来的rdb文件发送给多个重连的从节点。

数据部分复制

当主节点和从节点断开重连后,一般都会对整份数据进行复制。但从Redis2.8版本开始,Redis改用可以支持部分数据复制的命令PSYNC去主节点同步数据,从节点与主节点能够在网络连接断开后重连只进行断线后数据变化的增量更新。
主节点会在内存中创建一个缓存队列,缓存最近一段时间更新数据的命令,主节点和它所有的从节点都维护了更新数据命令的下标offset和主节点的进程ID。当网络连接断开后,从节点根据下标向主节点同步断线期间未执行的命令。如果主节点进程ID变化了,或者从节点数据下标offset太旧,已经不在主节点的缓存队列里了,那么将会进行一次全量数据的复制。

主从复制(部分复制,断点续传)流程图:

如果有很多从节点,为了缓解主从复制风暴(多个从节点同时复制主节点导致主节点压力过大),可以做如 下架构,让部分从节点与从节点(与主节点同步)同步数据:

Redis哨兵高可用架构

哨兵(sentinel)是特殊的Redis服务,不提供读写,主要用来监控Redis实例节点。

哨兵架构下的客户端在第一次从哨兵获得Redis主节点后,后续就直接访问Redis的主节点,不需要每次都通过哨兵访问Redis主节点。当Redis主节点有变化时,哨兵会第一时间感知到,并且将新的Redis主节点通知给客户端(这里面Redis客户端一般都实现了订阅功能,订阅sentinel发布的节点变动消息)。

下面,我们来搭建哨兵集群,我们先将sentinel.conf复制一份到config目录下,并修改其命名:

[root@master redis-6.2.1]# cp sentinel.conf config/
[root@master redis-6.2.1]# cd config/
[root@master config]# cp sentinel.conf sentinel-26379.conf

  

之后我们修改sentinel-26379.conf的配置,其中大部分配置和原先redis.conf的配置一样,这里就不再针对各个配置作解释了:

protected-mode no
#<1>
port 26379
daemonize yes
#<2>
pidfile /var/run/redis-sentinel-26379.pid
#<3>
logfile "26379.log"
dir /home/lf/redis-6.2.1/data
# sentinel monitor <master-name> <ip> <redis-port> <quorum>
#<ip>:哨兵集群监听的主节点IP
#<redis-port>:监听主节点IP的端口号
#<master-name>:哨兵集群可以监控多个Redis主从集群,设定Redis主节点便于客户端访问
#<quorum>:指明有多少个哨兵认为主节点失效时才算真正失效,值一般为:sentinel总数/2 +1
sentinel monitor mymaster 192.168.6.86 6379 2
#配置访问主节点的密码
sentinel auth-pass mymaster 123456

  

修改完sentinel-26379.conf后,我们复制两份配置分别命名为:sentinel-26380.conf和sentinel-26381.conf,并将上述<1>、<2>、<3>处26379分别改为26380和26381:

[root@master config]# cp sentinel-26379.conf sentinel-26380.conf
[root@master config]# cp sentinel-26379.conf sentinel-26381.conf

  

之后,我们根据这三份配置启动Redis哨兵服务:

[root@master redis-6.2.1]# src/redis-sentinel config/sentinel-26379.conf
[root@master redis-6.2.1]# src/redis-sentinel config/sentinel-26380.conf
[root@master redis-6.2.1]# src/redis-sentinel config/sentinel-26381.conf

  

启动完毕后,配置文件会自动补充主节点名称对应从节点的IP端口以及其他哨兵进程的IP和端口:

[root@master config]# cat sentinel-26379.conf
……
sentinel known-replica mymaster 192.168.6.86 6381
sentinel known-replica mymaster 192.168.6.86 6380
sentinel known-sentinel mymaster 192.168.6.86 26381 55d5eb86a5f220d802d08b1fff2d02f3433023ff
sentinel known-sentinel mymaster 192.168.6.86 26380 dc3444e290b9be39413364211a316404a8914e9c
[root@master config]# cat sentinel-26380.conf
……
sentinel known-replica mymaster 192.168.6.86 6381
sentinel known-replica mymaster 192.168.6.86 6380
sentinel known-sentinel mymaster 192.168.6.86 26379 6c62a2a7a9da058792b041ec5f7fb488c8674c74
sentinel known-sentinel mymaster 192.168.6.86 26381 55d5eb86a5f220d802d08b1fff2d02f3433023ff
[root@master config]# cat sentinel-26381.conf
……
sentinel known-replica mymaster 192.168.6.86 6381
sentinel known-replica mymaster 192.168.6.86 6380
sentinel known-sentinel mymaster 192.168.6.86 26380 dc3444e290b9be39413364211a316404a8914e9c
sentinel known-sentinel mymaster 192.168.6.86 26379 6c62a2a7a9da058792b041ec5f7fb488c8674c74

  

到目前为止,我们终于搭建完主从集群还有哨兵集群,现在,我们尝试根据主节点名称从哨兵集群获取主节点和从节点信息:

from redis.sentinel import Sentinel

sentinel = Sentinel([('192.168.6.86', 26379),
('192.168.6.86', 26380),
('192.168.6.86', 26381),
],
socket_timeout=0.5)
master = sentinel.discover_master('mymaster')
print("master:", master)
slave = sentinel.discover_slaves('mymaster')
print("slave:", slave)

      

执行结果:

master: ('192.168.6.86', 6379)
slave: [('192.168.6.86', 6380), ('192.168.6.86', 6381)]

    

之后,我们尝试用向主节点写数据,再通过从节点读取数据:

from redis.sentinel import Sentinel

sentinel = Sentinel([('192.168.6.86', 26379),
('192.168.6.86', 26380),
('192.168.6.86', 26381),
],
socket_timeout=0.5)
master = sentinel.discover_master('mymaster')
slave = sentinel.discover_slaves('mymaster')
master = sentinel.master_for('mymaster', socket_timeout=2, password='123456', db=0)
# 向主节点接数据
w_ret = master.set('hello', 'python')
slave = sentinel.slave_for('mymaster', socket_timeout=2, password='123456', db=0)
# 通过从节点读取数据
r_ret = slave.get('hello')
print(r_ret)

      

执行结果:

b'python'

  

最后,我们要验证哨兵集群的特性,就是如果主节点下线,哨兵集群会选举从节点成为主节点,我们在<1>、<2>处打印原先的主节点和从节点,在<3>处有一个循环往Redis主节点设置<fooN,barN>键值对,每设置完一次不管是否有抛出异常,都会休眠1s,这里会循环120次,总共休眠120s,在这120s内,我们将杀死Redis主节点,再看看哨兵集群是否会自动帮我们选取主节点,如果有选取出最新的主节点,在最后的<4>、<5>处会重新打印主节点和从节点信息:

from redis.sentinel import Sentinel
import time sentinel = Sentinel([('192.168.6.86', 26379),
('192.168.6.86', 26380),
('192.168.6.86', 26381),
],
socket_timeout=0.5)
master = sentinel.discover_master('mymaster')
print("origin master:", master) # <1>
slave = sentinel.discover_slaves('mymaster')
print("origin slave:", slave) # <2>
master = sentinel.master_for('mymaster', socket_timeout=2, password='123456', db=0)
for i in range(120): # <3>
try:
foo = 'foo' + str(i)
bar = 'bar' + str(i)
master.set(foo, bar)
print("set %s %s" % (foo, bar))
time.sleep(1)
except Exception as e:
print("Exception:", e)
time.sleep(1) master = sentinel.discover_master('mymaster')
print("new master:", master) # <4>
slave = sentinel.discover_slaves('mymaster')
print("new slave:", slave) # <5>

  

运行上面的代码,在10多秒时我们杀死端口号为6379的进程,即我们的Redis主节点:

[root@master redis-6.2.1]# ps -ef | grep redis
root 117641 1 0 08:23 ? 00:02:18 src/redis-server *:6379
root 117647 1 0 08:23 ? 00:02:16 src/redis-server *:6380
root 117654 1 0 08:24 ? 00:02:21 src/redis-server *:6381
root 117665 16267 0 08:24 pts/1 00:00:00 src/redis-cli
root 117666 22426 0 08:24 pts/0 00:00:00 src/redis-cli -p 6380
root 130214 1 0 16:56 ? 00:00:06 src/redis-sentinel *:26379 [sentinel]
root 130220 1 0 16:56 ? 00:00:06 src/redis-sentinel *:26380 [sentinel]
root 130226 1 0 16:57 ? 00:00:06 src/redis-sentinel *:26381 [sentinel]
root 130523 48986 0 17:08 pts/3 00:00:00 grep --color=auto redis
[root@master redis-6.2.1]# kill -9 117641

    

执行结果:

origin master: ('192.168.6.86', 6379)
origin slave: [('192.168.6.86', 6380), ('192.168.6.86', 6381)]
set foo0 bar0
set foo1 bar1
……
set foo17 bar17
Exception: Timeout connecting to server
Exception: Timeout connecting to server
……
Exception: Timeout connecting to server
set foo28 bar28
……
set foo117 bar117
set foo118 bar118
set foo119 bar119
new master: ('192.168.6.86', 6380)
new slave: [('192.168.6.86', 6381)]

  

现在我们来分析上面的运行结果,我们在打印了Redis主从节点的信息后,开始循环向Redis设置键值对,每次设置会休眠1s,在循环执行到第17次的时候,杀死端口号为6379的进程,之后代码再尝试向Redis主节点设置键值对就开始抛异常:Timeout connecting to server表示连接Redis服务超时,抛异常后会休眠1s,大致在几十秒内,哨兵集群判定Redis主节点下线,并重新选举出新的主节点,我们才能将<foo28,bar28>存储进新的Redis主节点。

最后程序重新打印Redis主从节点信息,可以看到主从节点信息已经跟以前不一样了,端口号为6380的从节点在哨兵集群判定主节点下线后,被选举为主节点,端口号6381的Redis服务依然为从节点。

我们连接6380Redis服务,查看Redis信息,可以看到6380服务当前的角色为主节点,有一个6381的从节点正连接到它。

# Replication
role:master
connected_slaves:1
slave0:ip=192.168.6.86,port=6381,state=online,offset=227449,lag=1
master_failover_state:no-failover
master_replid:2cb865fd2e68bb7b6cd9a3905cab2bc9aa115c02
master_replid2:7ee936b581b51f4b389b68cc3dcfcb3737468d5c
master_repl_offset:227588
second_repl_offset:185259
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:227588

  

如果大家还有印象,之前我们在redis-6380.conf和redis-6381.conf两份配置里,配置了主节点的信息,如果打印这两份配置的主节点信息,我们会发现,之前配置在redis-6380.conf的主节点信息已经不存在了,因为6380服务已经成为主节点,而redis-6381.conf从原先的192.168.6.86 6379改为192.168.6.86 6380:

[root@master config]# cat redis-6380.conf | grep replicaof
# Master-Replica replication. Use replicaof to make a Redis instance a copy of
[root@master config]# cat redis-6381.conf | grep replicaof
# Master-Replica replication. Use replicaof to make a Redis instance a copy of
replicaof 192.168.6.86 6380

而哨兵集群监控的主节点,也从我们原先设定的6379切换成6380

[root@master config]# cat sentinel-26379.conf | grep mymaster
sentinel monitor mymaster 192.168.6.86 6380 2
……
[root@master config]# cat sentinel-26380.conf | grep mymaster
sentinel monitor mymaster 192.168.6.86 6380 2
……
[root@master config]# cat sentinel-26381.conf | grep mymaster
sentinel monitor mymaster 192.168.6.86 6380 2
……

    

至此,笔者带大家了解了如何搭建主从&哨兵集群,以及哨兵集群的特性。大家私下也可以试着搭建一下主从&哨兵集群,有任何疑问也可以对照下载笔者的配置文件进行配置。

Redis主从&哨兵集群搭建的更多相关文章

  1. 【Redis学习专题】- Redis主从+哨兵集群部署

    集群版本: redis-4.0.14 集群节点: 节点角色 IP redis-master 10.100.8.21 redis-slave1 10.100.8.22 redis-slave2 10.1 ...

  2. 三千字介绍Redis主从+哨兵+集群

    一.Redis持久化策略 1.RDB 每隔几分钟或者一段时间会将redis内存中的数据全量的写入到一个文件中去. 优点: 因为他是每隔一段时间的全量备份,代表了每个时间段的数据.所以适合做冷备份. R ...

  3. redis主从和集群搭建

    主从搭建 redis的主从搭建非常简单,打开配置文件6379.conf,只需要将主节点的protected-mode设置为no,然后在从节点配置中加入:slaveof <masterip> ...

  4. redis主从与集群搭建

    redis搭建主从 条件:yum安装(3.2.1)与编译安装(5.0.0)都可以 环境:我这里在同一台主机上搭建,当然也可以两台. 1) 复制redis.conf的主配置文件并命令为slave.con ...

  5. redis主从、集群、哨兵

    redis的主从.集群.哨兵 参考: https://blog.csdn.net/robertohuang/article/details/70741575 https://blog.csdn.net ...

  6. Redis 3.0 集群搭建

    Redis 3.0 集群搭建 开启两个虚拟机 分别在两个虚拟机上开启3个Redis实例 3主3从两个虚拟机里的实例互为主备 下面分别在两个虚拟机上安装,网络设置参照codis集群的前两个主机 分别关闭 ...

  7. Redis 5.0 集群搭建

    Redis 5.0 集群搭建 单机版的 Redis 搭建 https://www.jianshu.com/p/b68e68bbd725 /usr/local/目录 mkdir redis-cluste ...

  8. redis哨兵集群搭建

    下载redis jar包redis-4.0.11.tar.gz放在/data/redis目录下 解压 命令:tar -zxvf redis-4.0.11.tar.gz 解压后如图所示 在/usr/lo ...

  9. redis的哨兵集群,redis-cluster

    #主从同步redis主从优先1.保证数据安全,主从机器两份数据一主多从2.读写分离,缓解主库压力主redis,可读可写slave身份,只读   缺点1.手动主从切换假如主库挂了,得手动切换master ...

随机推荐

  1. post upload file & application/x-www-form-urlencoded & multipart/form-data

    post upload file application/x-www-form-urlencoded & multipart/form-data https://stackoverflow.c ...

  2. 人物传记-BILL RAY:低谷时的信念,决定你能走多远

    自2018年全球经济危机以来,以工作为重的成年人们一直备受打击.尤其是2020年,全球贸易争端使得经济下滑严重,很多公司倒闭破产,有些人甚至从富豪变成了负债者,走向了人生低谷.其实,每个人都会遇到人生 ...

  3. NGK内存将为全球投资者创造新的财富增长机会

    2020年,随着BTC的持续上涨带动了整个区块链市场的持续加温,同时金融市场也对金融体制做出了改变,关于金融和区块链的结合越来越被人们所认可,在此基础上,DeFi行业借此迎来了快速发展,据不完全统计, ...

  4. ASP.NET Core中如何对不同类型的用户进行区别限流

    老板提出了一个新需求,从某某天起,免费用户每天只能查询100次,收费用户100W次. 这是一个限流问题,聪明的你也一定想到了如何去做:记录用户每一天的查询次数,然后根据当前用户的类型使用不同的数字做比 ...

  5. 创建gitHub账户并配置秘钥

    1. 登录注册地址 https://github.com/ 2.点击注册 Sign up 3.输入邮箱 密码 进行注册 4.注册成功后,登录邮箱验证 .然后通过邮箱和密码登录gitHub.设置 set ...

  6. Dyno-queues 分布式延迟队列 之 生产消费

    Dyno-queues 分布式延迟队列 之 生产消费 目录 Dyno-queues 分布式延迟队列 之 生产消费 0x00 摘要 0x01 前情回顾 1.1 设计目标 1.2 选型思路 0x02 产生 ...

  7. HTTP 1.x 学习笔记 —— Web 性能权威指南

    HTTP 1.0的优化策略非常简单,就一句话:升级到HTTP 1.1.完了! 改进HTTP的性能是HTTP 1.1工作组的一个重要目标,后来这个版本也引入了大量增强性能的重要特性,其中一些大家比较熟知 ...

  8. JS相关基础

    1. ES5和ES6继承方式区别 ES5定义类以函数形式, 以prototype来实现继承 ES6以class形式定义类, 以extend形式继承 2. Generator了解 ES6 提供的一种异步 ...

  9. 后台用JSONObject接收前端传过来的字符串数组,并转成集合(JSONObject---JSONArray---List)

    前端传递数据: handleSubmit() {this.dialogVisible = false; const param = { 'bidSampleImgList': this.fileLis ...

  10. cxf实例异常

    基于CXF2.3.0 Caused by: java.lang.InstantiationException: org.apache.cxf.wstx_msv_validation.WoodstoxV ...