在分布式系统中,希望使用多个服务器来部署redis,存在以下几种redis的部署方式
- 主从模式
- 主从+哨兵
- 集群模式
主从模式
在若干个redis节点中,有的是主节点,有的是从节点
假设有三个物理服务器(称为是三个节点)
分别部署了一个redis-server进程,此时就可以把其中的一个节点作为主节点,另外两个作为从节点,从节点的数据跟随主节点变化,从节点的数据要和主节点保持一致,如果改了从节点的数据,不可以把从节点的数据同步到主节点中
如果从节点挂了,没有什么影响,但如果是主节点挂了,有一定影响,因为从节点只能读取数据,如果要写数据就没有节点可以写了,如果我们设置多个主节点的话,数据同步会比较麻烦
操作
正常来说,每个redis服务器程序,应该是在一个单独的主机上,由于我没有多个服务器,我就在一个服务器运行多个redis-server进程,此时需要保证多个redis-server端口是不同的
redis启动时指定启动端口的方法:
- 启动的时候,通过命令行来指定端口,- -port选项
- 直接在配置文件中,设定端口
我们把/etc/redis/redis.conf拷贝到一个目录下,然后对这两个配置文件进行修改
把slave1.conf中的port改为6380,daemonize改为yes(后台运行),slave2.conf中的port改为6381,daemonize改为yes
启动redis
使用redis-cli -p 6380,redis-cli -p 6381还有redis-cli启动三个redis,这个时候在主节点(6379)端口set key 111,6380端口的redis并不能看到,所以这个时候还没有达成主从复制
建立复制
想要达成主从复制有三种方式:
- 在配置⽂件中加⼊ slaveof {masterHost} {masterPort} 随 Redis 启动⽣效。→永久生效
- 在 redis-server 启动命令时加⼊ --slaveof {masterHost} {masterPort} ⽣效。
- 直接使⽤ redis 命令(在客户端中):slaveof {masterHost} {masterPort} ⽣效。→重启后就按照配置文件中的方案走
这里我们采用第一种方案:vim salve1.conf和vim salve2.conf,在最后面添加,这里以6379为主节点
这时候重新启动三个redis服务器才可以完成主从复制,通过 redis-cli 可以连接主 Redis 实例,通过 redis-cli -p 6380 连接从 Redis。并且观察复制关系。
127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> get hello
"world"
127.0.0.1:6380> get hello
"world"
从运⾏结果中看到复制已经⼯作了,针对主节点 6379 的任何修改都可以同步到从节点 6380 中,复制过程如图所示。
使用netstat -anp | grep redis-server查看redis进程
又上图可知,其实主从复制就是主节点和从节点创建TCP连接
1)主节点6379复制状态信息,用info replication查看
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=6380,state=online,offset=100,lag=0
master_replid:2fbd35a8b8401b22eb92ff49ad5e42250b3e7a06
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:100
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:100
2)从节点 6380 复制状态信息
127.0.0.1:6380> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_repl_offset:170
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:2fbd35a8b8401b22eb92ff49ad5e42250b3e7a06
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:170
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:170
这里介绍几个选项的含义:
- offset:相当于是从节点和主节点之间,同步数据的进度,因为主节点上会收到源源不断的"修改数据”请求,从节点就需要从主节点这里同步这些修改请求,从节点和主节点之问的数据同步,不是瞬问完成的!!
- lag:延迟
- master_replid:主节点的身份标识
- master_replid2:如果master节点宕机了,slave节点成功切换为master后,会将之前master_replid记录的值存储到master_replid2中,自己生成一个新的随机字符,作为自己的master标识,存储在master_replid中
- master_repl_offset:主节点数据修改了多少
- repl_backlog_active/repl_backlog_size/repl_backlog_first_byte_offset/repl_backlog_histlen:积压缓冲区,支持部分同步机制的实现
- slave_priority:主节点挂了,选择从节点作为主节点的优先级
- slave_read_only:从节点是否只读
- connected_slaves:从节点下面也可以再有从节点,也可以用从节点去同步从节点
关于master_replid和master_replid2可以看这篇文章:replication中的两个master_replid
断开复制
slaveof 命令不但可以建⽴复制,还可以在从节点执⾏ slaveof no one(在客户端中输入)来断开与主节点复制关系。
例如在 6380 节点上执⾏ slaveof no one 来断开复制。
断开复制主要流程:
1)断开与主节点复制关系。
2)从节点晋升为主节点。
从节点断开复制后并不会抛弃原有数据,只是⽆法再获取主节点上的数据变化。
通过 slaveof 命令还可以实现切主操作,将当前从节点的数据源切换到另⼀个主节点。执⾏
slaveof {newMasterIp} {newMasterPort} 命令即可。
切主操作主要流程:
1)断开与旧主节点复制关系。
2)与新主节点建⽴复制关系。
3)删除从节点当前所有数据。
4)从新主节点进⾏复制操作。
安全性
对于数据⽐较重要的节点,主节点会通过设置 requirepass 参数进⾏密码验证,这时所有的客户
端访问必须使⽤ auth 命令实⾏校验。从节点与主节点的复制连接是通过⼀个特殊标识的客户端来完
成,因此需要配置从节点的masterauth 参数与主节点密码保持⼀致,这样从节点才可以正确地连接到主节点并发起复制流程。
只读
默认情况下,从节点使⽤ slave-read-only=yes 配置为只读模式。**由于复制只能从主节点到从节
点,对于从节点的任何修改主节点都⽆法感知,修改从节点会造成主从数据不⼀致。**所以建议线上不
要修改从节点的只读模式。
传输延迟
主节点和从节点之间通过网络来传输(TCP),TCP内部支持了nagle算法(默认开启)
开启:会增加tcp的传输延迟,节省了网络带宽
关闭:会减少网络延迟,增加了网络带宽
这个nagle算法和捎带应答目的是一样的,针对较小的数据包进行合并,减少了包的个数
redis中repl-disable-tcp-nodelay选项用于在主从同步通信过程中,关闭tcp 的nagle 算法,从节点更快速的和主节点进行同步
拓扑结构
一主一从结构
⼀主⼀从结构是最简单的复制拓扑结构,⽤于主节点出现宕机时从节点提供故障转移⽀持,如图
所示。当应⽤写命令并发量较⾼且需要持久化时,可以只在从节点上开启 AOF,这样既可以保证数据安全性同时也避免了持久化对主节点的性能⼲扰。但需要注意的是,当主节点关闭持久化功能时,如果主节点宕机要避免⾃动重启操作。(如果自动重启,此时没有AOF文件,就会丢失数据,进一步的主从同步,会把从节点的数据也删除掉)
一主多从
⼀主多从结构(星形结构)使得应⽤端可以利⽤多个从节点实现读写分离。对于读⽐重较⼤的场景,可以把读命令负载均衡到不同的从节点上来分担压⼒。同时⼀些耗时的读命令可以指定⼀台专⻔的从节点执⾏,避免破坏整体的稳定性。对于写并发量较⾼的场景,多个从节点会导致主节点写命令的多次发送从⽽加重主节点的负载。(主节点上数据的修改,就会把改变的数据同步到从节点)
树形主从结构
树形主从结构(分层结构)使得从节点不但可以复制主节点数据,同时可以作为其他从节点的主
节点继续向下层复制。通过引⼊复制中间层,可以有效降低住系欸按负载和需要传送给从节点的数据
量数据写⼊节点 A 之后会同步给 B 和 C 节点,B 节点进⼀步把数据同步给 D 和 E 节点。当主节点需要挂载等多个从节点时为了避免对主节点的性能⼲扰,可以采⽤这种拓扑结构。
原理
如图所示,下⾯详细介绍建⽴复制的完整流程。从图中可以看出复制过程⼤致分为 6 个过程:
主从节点建⽴复制流程图
1)保存主节点(master)的信息。
开始配置主从同步关系之后,从节点只保存主节点的地址信息,此时建⽴复制流程还没有开始,
在从节点 6380 执⾏ info replication 可以看到如下信息:
master_host: 127.0.0.1
master_port: 6379
master_link_status: down
从统计信息可以看出,主节点的 ip 和 port 被保存下来,但是主节点的连接状态(master_link_status)是下线状态。
2)从节点(slave)内部通过每秒运⾏的定时任务维护复制相关逻辑,当定时任务发现存在新的主节
点后,会尝试与主节点建⽴基于 TCP 的⽹络连接。如果从节点⽆法建⽴连接,定时任务会⽆限重试直到连接成功或者⽤户停⽌主从复制。(TCP三次握手验证通信双方是否能正确读取数据,系统层面)
3)发送 ping 命令。连接建⽴成功之后,从节点通过 ping 命令确认主节点在应⽤层上是⼯作良好的。如果 ping 命令的结果 pong 回复超时,从节点会断开 TCP 连接,等待定时任务下次重新建⽴连接。
4)权限验证。如果主节点设置了 requirepass 参数,则需要密码验证,从节点通过配置 masterauth
参数来设置密码。如果验证失败,则从节点的复制将会停⽌。
5)同步数据集。对于⾸次建⽴复制的场景,主节点会把当前持有的所有数据全部发送给从节点,这步操作基本是耗时最⻓的,所以⼜划分称两种情况:全量同步和部分同步,下⼀节重点介绍。
6)命令持续复制。当从节点复制了主节点的所有数据之后,针对之后的修改命令,主节点会持续的把命令发送给从节点,从节点执⾏修改命令,保证主从数据的⼀致性。
数据同步
Redis 使⽤ psync 命令完成主从数据同步,同步过程分为:全量复制和部分复制。
- 全量复制:⼀般⽤于初次复制场景,Redis 早期⽀持的复制功能只有全量复制,它会把主节点全部数据⼀次性发送给从节点,当数据量较⼤时,会对主从节点和⽹络造成很⼤的开销。
- 部分复制:⽤于处理在主从复制中因⽹络闪断等原因造成的数据丢失场景,当从节点再次连上主节点后,如果条件允许,主节点会补发数据给从节点。因为补发的数据远⼩于全量数据,可以有效避免全量复制的过⾼开销。
- 实时复制:从节点,已经和主节点,同步好了数据了(从节点这一时刻已经和主节点数据一致了)但是之后,主节点这边会源源不断的收到新的修改数据的请求,主节点上的数据就会随之改变,也需要能够同步给从节点
从节点使⽤ psync 命令完成部分复制和全量复制功能,命令格式:psync {replicationId} {offset},参数含义:replicationId 是从节点所复制的主节点的运⾏ ID、offset 是当前从节点已复制的数据偏移量。
全量复制
1)从节点发送 psync 命令给主节点,replicationId 和 offset 的默认值分别是 ? 和 -1。
2)主节点根据 psync 参数和⾃⾝数据情况决定响应结果:
- 如果回复 +FULLRESYNC replicationId offset,则从节点需要进⾏全量复制流程。
- 如果回复 +CONTINEU,从节点进⾏部分复制流程。
- 如果回复 -ERR,说明 Redis 主节点版本过低,不⽀持 psync 命令。从节点可以使⽤ sync 命令进⾏全量复制。
psync 并不需要咱们⼿动执⾏,Redis 会在主从复制模式下⾃动调⽤执⾏。
1)从节点发送 psync 命令给主节点进⾏数据同步,由于是第⼀次进⾏复制,从节点没有主节点的运⾏ ID 和复制偏移量,所以发送 psync ? -1。
2)主节点根据命令,解析出要进⾏全量复制,回复 +FULLRESYNC 响应。
3)从节点接收主节点的运⾏信息进⾏保存。
4)主节点执⾏ bgsave 进⾏ RDB ⽂件的持久化。
5)主节点发送 RDB ⽂件给从节点,从节点保存 RDB 数据到本地。
6)主节点将从⽣成 RDB 到接收完成期间执⾏的写命令,写⼊缓冲区中,等从节点加载完 RDB ⽂件后,主节点再将缓冲区内的数据补发给从节点,保持主从⼀致性。
7)从节点清空⾃⾝原有旧数据。
8)从节点加载 RDB ⽂件得到与主节点⼀致的数据。
9)如果从节点加载 RDB 完成之后,并且开启了 AOF 持久化功能,它会进⾏ bgrewrite 操作,得到最近的 AOF ⽂件。
由于当前收到的是大批量的数据,此时产生的 aof 日志,整体来说,可能会存在一定的冗余信息,因此针对 aof 日志进行整理,也是必要的过程
Redis Bgrewriteaof 命令用于异步执行一个 AOF(AppendOnly File) 文件重写操作。重写会创建一个当前 AOF 文件的体积优化版本。
即使 Bgrewriteaof 执行失败,也不会有任何数据丢失,因为旧的 AOF 文件在 Bgrewriteaof 成功之前不会被修改。
无硬盘模式:主节点生成的rdb二进制数据,不直接保存到文件中了,而是直接进行网络传输(节省了一系列的读硬盘和写硬盘的操作),从节点,直接把收到的数据进行加载
部分复制
部分复制主要是 Redis 针对全量复制的过⾼开销做出的⼀种优化措施,使⽤ psync replicationId offset 命令实现。当从节点正在复制主节点时,如果出现⽹络闪断或者命令丢失等异常情况时,从节点会向主节点要求补发丢失的命令数据,如果主节点的复制积压缓冲区存在数据则直接发送给从节点,这样就可以保持主从节点复制的⼀致性。补发的这部分数据⼀般远远⼩于全量数据,所以开销很⼩。整体流程如图所示。
1)当主从节点之间出现⽹络中断时,如果超过 repl-timeout 时间,主节点会认为从节点故障并终端 复制连接。
2)主从连接中断期间主节点依然响应命令,但这些复制命令都因⽹络中断⽆法及时发送给从节点,所以暂时将这些命令滞留在复制积压缓冲区中。
3)当主从节点⽹络恢复后,从节点再次连上主节点。
4)从节点将之前保存的replicationId 和 复制偏移量作为 psync 的参数发送给主节点,请求进⾏部分复制。
5)主节点接到 psync 请求后,进⾏必要的验证。随后根据 offset 去复制积压缓冲区查找合适的数据,并响应 +CONTINUE 给从节点。
6)主节点将需要从节点同步的数据发送给从节点,最终完成⼀致性。
积压缓冲区:
就是一个内存中的简单的队列,会记录最近一段时间修改的数据,总量有限,随着时间的推移,就会把之前的,旧的数据逐渐删掉。
replicationid 其实就是在描述"数据的来源",offset 描述"数据的复制的进度"
如果 replicationld 不一样,需要全量复制一下。
如果 replicationld 一样,offset 再判定。
offset 表示 从节点之前同步数据的进度是如何,主节点就看这个进度是否在当前的积压缓冲区之内
如果确实是在积压缓冲区之内,此时就可以直接进行部分复制,就只把最近这段时间的数据给复制过去即可,如果当前从节点的进度已经超出积压缓冲区的范围了,需要全量复制。
实时复制
从节点和主节点之间会建立 TCP 的长连接,然后主节点把自己收到的修改数据的请求,通过上述 连接,发给从节点从节点再根据这些修改请求,修改内存中的数据,这个过程也需要时间,正常来说延迟比较小,主从节点在建⽴复制连接后,它们之间需要维护⻓连接并彼此发送⼼跳命令。
1)主从节点彼此都有⼼跳检测机制,各⾃模拟成对⽅的客户端进⾏通信。
2)主节点默认每隔 10 秒对从节点发送 ping命令,判断从节点的存活性和连接状态。
3)从节点默认每隔 1 秒向主节点发送 replconf ack {offset} 命令,给主节点上报⾃⾝当前的复制偏移量。
如果主节点发现从节点通信延迟超过 repl-timeout 配置的值(默认 60 秒),则判定从节点下线,断开复制客户端连接。从节点恢复连接后,⼼跳机制继续进⾏
replication id/runid
多数资料对这两个的区分有所问题,一个redis服务器上,replication id和run id都是存在的
主节点:info replication
info server
从节点:info replication
info server
官方文档中psync replicationid offset而不是runid
runid主要是用在支持实现redis哨兵的功能的
主从复制总结
主从复制的特点:
- Redis 通过复制功能实现主节点的多个副本。
- 主节点⽤来写, 从节点⽤来读. 这样做可以降低主节点的访问压⼒.
- 复制⽀持多种拓扑结构,可以在适当的场景选择合适的拓扑结构。
- 复制分为全量复制和部分复制。
- 主从节点之间通过⼼跳机制保证主从节点通信正常和数据⼀致性。
主从复制的缺点:
- 从机多了, 复制数据的延时⾮常明显.
- 主机挂了, 从机不会升级成主机. 只能通过⼈⼯⼲预的⽅式恢复.
redis主节点无法重启
从节点和主节点断开连接有两种情况:
- 从节点主动和主节点断开连接:slaveof no one,这个时候从节点晋升为主节点
- 主节点挂了:从节点不会晋升为主节点,必须通过人工干预的方法,恢复主节点
这里有aof文件,这个文件是redis服务器重启的时候,需要加载的
在上文介绍中,我们三个redis服务器共用的是一个aof文件,这是错误的从节点是通过手动启动的方式运行的。 此时 root 用户下启动redis 服务器,于是生成的 aof 文件也就是root用户的文件。
通过service redis-server start启动的redis服务器,是通过redis这样的用户来启动的,redis server 需要按照可读可写的方式打开这个 aof 文件,而这个文件对于 root 之外的用户只有读权限,因此 service redis-server start 启动的redis 服务器无法打开这个文件,就启动失败了。
解决方法:
- 把三个不同的redis服务器生成的文件区分开
- 把三个redis服务器的工作目录区分开(修改配置文件中的dir选项)
这里采用第二种方案演示:
- 停止所有redis服务器
- 删除之前工作目录下已经生成的aof文件,或者chown更改权限
- 给从节点创建出新的目录,用来作为从节点的工作目录
vim slave1.conf和slave2.conf修改dir选项为/root/redis-conf