Redis主从模式研究
Redis主从模式研究
Redis 目前部署的方式有单机模式、主从模式、集群模式、哨兵模式,接下来我准备逐个对这几个模式简单的研究下。
研究的点:
- 模式的特点是什么。
- 模式解决了什么问题。
- 模式的简单原理是什么。
- 尝试搭建一套对应模式的环境,进行一些测试等。
- 概括下这种模式的优缺点。
1. 单节点模式特点以及缺点
Redis 的单节点模式是最基础的部署方式,所谓的单节点就是指所有数据都保存在一个 Redis 实例上。 所有的读写操作也都是通过这个实例来完成。
这种模式在配置以及管理上都相对简单,而且单节点的情况下考虑的事情还比较少。 比如不用考虑数据同步问题、状态一致性等这些问题。
但是这些是单机模式的优点,同时也可以说是单机模式的缺点。 由于单机模式只有一个 Redis 节点,如果这个 Redis 节点一旦发生故障,那么就会导致服务不可用。 同时单个 Redis 节点处理请求的能力是有上限的,请求并发量超过 Redis 节点处理能力的时候,请求就会被阻塞住,影响服务的性能。单个节点存储的数据量还会是有上限的。
2. 单节点模式的缺点怎么去解决
从上面的描述中,可以总结出几点缺点:
- Redis 服务可靠性问题。
- Redis 容量问题。
- Redis 并发请求处理瓶颈。
上面的几个问题在业界很早之前就已经有了解决方案,同时 Redis 官方也有相对应的的方案。
服务可靠性问题可以通过部署多个节点来解决,一个节点不行就两个,两个不行就四个。 只要备份的节点多,服务就会变得相对可靠。
容器问题也是一样的,一个节点容量有上限,那么就多部署几个节点去平摊大数据,这样就可以解决这个问题。
并发请求处理瓶颈也是通过部署多个节点,将请求平摊到多个节点上去。
上面上的几个方案,在 Redis 里面都是对应的一个部署模式:
- 主从模式,为了解决单节点故障问题,同时还可以解决并发请求处理瓶颈的问题。
- 集群模式,为了解决单个节点数据量大的问题,也解决节点故障问题。
- 哨兵模式,哨兵模式主要是为了解决节点故障问题。 通常是跟上面两个模式组合使用。
3. 主从模式是什么
上面说了,主从模式可以解决单点故障问题,也可以解决并发请求处理瓶颈的问题。
这种模式下,一个 Redis 节点作为主节点 ( Master ) ,这个节点负责处理所有的写操作。 当然这个节点也可以处理读操作,不过一般主节点只会处理写。
一个或者多个 Redis 节点作为从节点 ( Slave) ,这些节点会定时去向主节点发起同步数据请求,然后接收主节点发送过来的数据完成数据的同步。这样做的优点是从节点可以灵活的控制数据同步的时机跟频率。
数据复制 ( Data Replication ) 指的就是上面从节点跟主节点之间数据同步的过程。 数据同步的过程是异步的,不会阻塞主节点的请求处理。
比较常见的主从模式如下:
上面说的这种是主节点下面是从节点,在实际的生产环境中,也会出现从节点下面再有从节点的情况。
所以主从模式的就是通过将数据完整的复制到多个 Redis 从节点,来解决单节点可靠性问题的。 同时主从节点分别负责读跟写操作,这样可以提升 Redis 整体的并发请求处理能力。
注意: **Redis 主从模式中,主节点宕机后,不会自动选出主节点来。从节点可以继续提供读服务,但不能提供写服务。 **
4. 主从模式的原理
主从模式的核心原理主要就是主从节点的数据同步机制,这个机制要处理下面几个问题:
主从节点哪边先发起数据同步请求?
什么时候开始进行数据同步?数据同步的频率是多少?
数据同步的时候是将主节点的数据全部发送给从节点还是发送一部分数据给从节点?
数据以什么样的格式发送给从节点?
主从节点间出现网络故障怎么处理?
把上面几个问题给解决掉,基本主从模式就算实现的差不多了。目前版本的 Redis 做法如下:
从节点在第一次连接到主节点时, 会触发一个完整的同步过程。 首先是从节点通过向主节点发送 PSYNC 命令来请求数据同步,主节点会执行 bgsave 命令,将当前的数据生成一个 RDB 快照文件,然后将这个文件发送给从节点。从节点收到这个文件后,会先清空自己本地的数据,然后加载这个 RDB 文件,这样就相当于有了基础数据。
在第一次数据同步完成之后,后续主节点会将所有缓存的写操作命令以文本格式异步发送给从节点。 从节点接收到这些命令后,开始在本地执行这些命令,达到跟主节点数据保持一致的效果。
这个同步机制依赖于主节点在内存中维护的一个缓存区,缓冲区中记录最近的写命令信息。 当从节点重新连接的时候,主节点会将缓冲区的命令发送给从节点,保证数据的一致性。
上面的同步过程主要分为 2 种:
- 全量同步。全量同步就是主节点生成一个完整的 RDB 文件发送给从节点,实现数据的完全同步。 通常用于从节点首次连接主节点,或者从节点长时间没有连接到主节点后重新连接到主节点的情况。
- 增量同步。增量同步就是主从节点正常运行过程中,主节点发送一部分写命令给从节点。这样同步的效率高,大部分时候主从节点都是在执行增量同步。
5. 主从模式实践
搭建主从模式步骤也是比较少的,假设我们搭建个 1 个主节点 2 个从节点,主要分为以下几步:
在 3 台服务器上分别安装同一个版本的 Redis 。
先启动主节点的 Redis 服务。
在两个从节点的配置文件 redis.conf 里面,配置好主节点的地址,比如下面这样配置:
slaveof 192.168.60.1
注意如果主节点开启了密码认证的话,从节点需要配置下主节点的密码认证的。
- 分别启动 2 个从节点的 Redis 服务。
这样一个1主2从的 Redis 主从就算搭建完毕,可以通过命令去验证主从是否搭建成功。
假设我本地的节点分别为:
- 172.18.0.2 从节点1
- 172.18.0.3 主节点
- 172.18.0.4 从节点2
在主节点 172.18.0.3 执行 Redis 命令:
172.18.0.3:6379> info replication
# Replication
role:master # 这里的 master 表示该实例是主节点
connected_slaves:2 # 这里显示有 2 个从节点连接到该主节点
# 第一个从节点的连接信息,online 表示从节点正常在线, offset=1057表示从节点当前的复制偏移量。偏移量表示从节点已经同步到了主节点的哪个位置。偏移量越大,表示同步的数据越多。lag=1表示从节点的复制延迟是1秒。
slave0:ip=172.18.0.2,port=6379,state=online,offset=1057,lag=1
slave1:ip=172.18.0.4,port=6379,state=online,offset=1057,lag=1
# 主节点的复制 ID。这个 ID 是一个唯一的标识符,用来标识当前主节点的复制状态。当主节点发生故障切换或重新启动时,这个标识符可能会改变。
master_replid:71d0c478a1edd081a3de23681a831ed25bc978cc
# master_replid2 用于部分重同步。如果主节点发生了故障切换或重启,master_replid2 会保存上一个主节点的复制 ID。当前值为0说明没有发生过故障。
master_replid2:0000000000000000000000000000000000000000
# 主节点当前的复制偏移量。这个值表示主节点已经同步给从节点的数据量。这里的值现在是 1057,跟从节点的 offset 一样,说明主从节点的同步是完全一致的。
master_repl_offset:1057
# 记录了前一个主节点的复制偏移量。-1 表示当前没有有效的前一个主节点偏移量,因为没有重启过。
second_repl_offset:-1
repl_backlog_active:1 # 1 表示复制日志当前是激活状态
repl_backlog_size:1048576 # 复制日志的大小为 1 MB,这里是字节单位。
repl_backlog_first_byte_offset:1 # 复制日志中第一个字节的偏移量。
repl_backlog_histlen:1057 # 复制日志中当前存储的历史数据长度。
在从节点 1 执行 Redis 命令:
172.18.0.2:6379> info replication
# Replication
role:slave # 表示当前节点是从节点
master_host: 172.18.0.3 # 主节点ip
master_port:6379 # 主节点端口
master_link_status:up # 跟主节点的连接状态, up 是正常。
master_last_io_seconds_ago:9 # 表示从节点在 9 秒前最后一次从主节点接收到数据。
master_sync_in_progress:0 # 当前是否正在进行主从同步。0 表示没有同步操作正在进行,1 则意味着正在进行同步操作。
slave_repl_offset:4795 # 表示从节点已经接收并应用了主节点发送的 4795 字节的数据。
slave_priority:100 # 从节点的优先级,默认就是 100
slave_read_only:1 # 从节点是否处于只读模式。1 表示开启了只读模式,从节点只接受读请求,不接受写请求。
connected_slaves:0 # 当前从节点的从节点数量。我没有在从节点下设置从节点。
# 当前主节点的复制 ID
master_replid:71d0c478a1edd081a3de23681a831ed25bc978cc
# 如果主节点发生了故障切换或重启,master_replid2会保存上一个主节点的复制 ID。当前值为全0说明没有故障发生。
master_replid2:0000000000000000000000000000000000000000
# 主节点已经发送了 4795 字节的数据。
master_repl_offset:4795
second_repl_offset:-1 # 前一个主节点的复制偏移量,-1是没有发生过重启。
repl_backlog_active:1 # 复制日志是否是激活状态
repl_backlog_size:1048576 # 复制日志的大小为 1 MB,这里是字节
repl_backlog_first_byte_offset:1 # 复制日志中第一个字节的偏移量,这里是1 。
repl_backlog_histlen:4795 # 当前存储的历史数据长度,这里表示已经包含了 4795 字节数据。
在从节点 2 执行 Redis 命令:
172.18.0.4:6379> info replication
# Replication
role:slave
master_host: 172.18.0.3
master_port:6379
master_link_status:up
master_last_io_seconds_ago:8
master_sync_in_progress:0
slave_repl_offset:5369
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:71d0c478a1edd081a3de23681a831ed25bc978cc
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:5369
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:5369
这个节点的输出跟从节点 1 输出基本是一致的,上面这几个节点的输出表示主从节点已经搭建完毕。
可以写个简单的 Python 脚本验证下主从数据同步:
import time
import redis
master = redis.Redis(host='172.18.0.3', port=6379)
slaves = [redis.Redis(host='172.18.0.2', port=6379), redis.Redis(host='172.18.0.4', port=6379)]
test_key = 'test_key'
test_value = 'test_value'
if __name__ == "__main__":
print(f"往主节点写入数据: {test_key} -> {test_value}")
master.set(test_key, test_value)
print(f"休眠1秒等待主从节点同步数据")
time.sleep(1)
print(f"开始验证从节点是否按照预期同步主节点数据")
for i, slave in enumerate(slaves):
value = slave.get(test_key)
if value is not None:
value = value.decode('utf-8')
if value == test_value:
print(f"数据已经同步到从节点 {i + 1}.")
else:
print(f"数据未能正确同步到从节点 {i + 1}.")
print(f"Expected: {test_value}, but got: {value}")
运行后正常应该控制台输出:
往主节点写入数据: test_key -> test_value
休眠1秒等待主从节点同步数据
开始验证从节点是否按照预期同步主节点数据
数据已经同步到从节点 1.
数据已经同步到从节点 2.
6. 主从模式总结
主从模式的优点:
- 可以读写分离,写请求交由主节点处理,读请求交给从节点处理。
- 从节点保存了主节点的数据,相当于数据冗余,防止单点故障。
- 主节点故障的时候,可以把从节点提升为新的主节点。
主从模式也有自己的不足:
- 主从复制是异步的,所以主节点写完之后,不会立马同步到从节点,这里会有短时间的数据一致性问题。
- 写操作多的话,主从模式依旧没能解决性能的问题。
- 主从模式下主节点不可用的话,从节点不会自动选举出主节点,这点需要注意。
适合的场景:
- 读的请求比较多,写的请求少的情况。
- 从节点充当主节点数据的一个备份。
主从模式加上自动主节点切换的话,可以满足大部分的业务场景。