Redis简单主从

发布于 2023-07-12  194 次阅读


目标:搭建一个Redis的简单主从

环境搭建

环境准备

我是使用docker compose简单的创建了3个Redis分别是Redis1(主)、Redis2(从)、Redis3(从)。

如果你是普通服务器,请准备3个Redis。

以下不使用Docker做演示,Docker只需要参考修改一下就可以非常简单。

创建配置/数据/日志目录

 # 创建配置目录
mkdir -p /usr/local/redis/conf
# 创建数据目录
mkdir -p /usr/local/redis/data
# 创建日志目录
mkdir -p /usr/local/redis/log

修改配置文件

创建或下载一份配置文件至 conf 目录。

按照Redis版本下载配置文件,下载地址:URL

vim /usr/local/redis/conf/redis.conf

分别修改3个节点配置文件中以下内容:

# 放行访问IP限制
bind 0.0.0.0
# 后台启动
daemonize yes
# 日志存储目录及日志文件名
logfile "/usr/local/redis/log/redis.log"
# rdb数据文件名
dbfilename dump.rdb
# aof模式开启和aof数据文件名
appendonly yes
appendfilename "appendonly.aof"
# rdb数据文件和aof数据文件的存储目录
dir /usr/local/redis/data
# 设置密码
requirepass 123456
# 从节点访问主节点密码(必须与 requirepass 一致)
masterauth 123456
# 从节点只读模式
replica-read-only yes

在从节点中额外添加以下内容:

# 下面的配置无需在主节点中配置
# 从节点属于哪个主节点
slaveof 192.168.10.101 6379

# 从节点属于哪个主节点 Docker
slaveof 容器名称(例如:redis1) 6379

启动

三个节点分别运行以下命令:

/usr/local/redis/bin/redis-server /usr/local/redis/conf/redis.conf

检查

每个节点自带的客户端连接至Redis服务。

/usr/local/redis/bin/redis-cli

通过 info replication 查看主从信息,检查环境是否搭建成功。

127.0.0.1:6379> info replication

然后在主节点插入一条数据,测试从节点是否可读取(是否复制成功),测试从节点是否可写数据(从节点只读模式是否生效)。


原理剖析

IP预角色
192.168.10.101Master
192.168.10.102Slave
192.168.10.103Slave

复制配置

配置基本的Redis复制功能是很简单的。只需要将以下的内容加进slave的配置文件:

# 从节点属于哪个主节点,从哪个主节点进行复制
slaveof 192.168.10.101 6379

info replication

主节点

127.0.0.1:6379> info replication
# Replication
# 角色
role:master
# 从节点的连接数
connected_slaves:2
# 从节点详细信息 IP PORT 状态 命令(单位:字节长度)偏移量 延迟秒数
# 主节点每次处理完写操作,会把命令的字节长度累加到master_repl_offset中。
# 从节点在接收到主节点发送的命令后,会累加记录子什么偏移量信息slave_repl_offset,同时,也会每秒钟上报自身的复制偏移量到主节点,以供主节点记录存储。
# 在实际应用中,可以通过对比主从复制偏移量信息来监控主从复制健康状况。
slave0:ip=192.168.10.102,port=6379,state=online,offset=23866,lag=0
slave1:ip=192.168.10.103,port=6379,state=online,offset=23866,lag=0
# master启动时生成的40位16进制的随机字符串,用来标识master节点
master_replid:acc2aaa1f0bb0fd79d7d3302f16bddcbe4add423
master_replid2:0000000000000000000000000000000000000000
# master 命令(单位:字节长度)已写入的偏移量
master_repl_offset:23866
second_repl_offset:-1
# 0/1:关闭/开启复制积压缓冲区标志(2.8+),主要用于增量复制及丢失命令补救
repl_backlog_active:1
# 缓冲区最大长度,默认 1M
repl_backlog_size:1048576
# 缓冲区起始偏移量
repl_backlog_first_byte_offset:1
# 缓冲区已存储的数据长度
repl_backlog_histlen:23866

从节点

127.0.0.1:6379> info replication
# Replication
# 角色
role:slave
# 主节点详细信息
master_host:192.168.10.101
master_port:6379
# slave端可查看它与master之间同步状态,当复制断开后表示down
master_link_status:up
# 主库多少秒未发送数据到从库
master_last_io_seconds_ago:1
# 从服务器是否在与主服务器进行同步 0否/1是
master_sync_in_progress:0
# slave复制命令(单位:字节长度)偏移量
slave_repl_offset:24076
# 选举时,成为主节点的优先级,数字越大优先级越高,0 永远不会成为主节点
slave_priority:100
# 从库是否设置只读,0读写/1只读
slave_read_only:1
# 连接的slave实例个数
connected_slaves:0
# master启动时生成的40位16进制的随机字符串,用来标识master节点
master_replid:acc2aaa1f0bb0fd79d7d3302f16bddcbe4add423
# slave切换master之后,会生成了自己的master标识,之前的master节点的标识存到了master_replid2的位置
master_replid2:0000000000000000000000000000000000000000
# master 命令(单位:字节长度)已写入的偏移量
master_repl_offset:24076
# 主从切换时记录主节点的命令偏移量+1,为了避免全量复制
second_repl_offset:-1
# 0/1:关闭/开启复制积压缓冲区标志(2.8+),主要用于增量复制及丢失命令补救
repl_backlog_active:1
# 缓冲区最大长度,默认 1M
repl_backlog_size:1048576
# 缓冲区起始偏移量
repl_backlog_first_byte_offset:1
# 缓冲区已存储的数据长度
repl_backlog_histlen:24076

复制流程

Redis的主从结构可以采用一主多从或者级联结构,Redis主从复制可以根据是否全量分为全量同步和增量同步

主从刚刚连接的时候,进行全量同步;全同步结束后,进行增量同步。

  • 全量同步
    • Redis全量复制一般发生在Slave初始化阶段,这时Slave需要将Master上的所有数据都复制一份
    • 从服务器连接主服务器,发送SYNC命令
    • 主服务器接收到了SYNC命名后,开始执行BGSAVE命令生成RDB文件并使用缓冲区记录此后执行的所有写命令
    • 主服务器BGSAVE执行完后,向所有从服务器发送快照文件,并在发送期间继续记录被执行的写命令
    • 从服务器收到快照文件后丢弃所有旧数据,载入收到的快照;
    • 主服务器快照发送完毕后开始向从服务器发送缓冲区中的写命令
    • 从服务器完成对快照的载入,开始接收命令请求,并执行来自主服务器缓冲区的写命令
  • 增量同步
    • Redis增量复制是指Slave初始化后开始正常工作时主服务器发生的写操作同步到从服务器的过程
    • 增量复制的过程主要是主服务器每执行一个写命令就会向从服务器发送相同的写命令,从服务器接收并执行收到的写命令
  • 主从复制的异步特性
    • 主从复制对于主Redis来说是非阻塞的
      • 这意味着当从服务器在进行主从复制同步过程中,主Redis仍然可以处理外接的访问请求
    • 主从复制对于从Redis服务器来说也是非阻塞的
      • 这意味着,即使从Redis在进行主从复制的过程中也可以接受外界的查询请求,只不过这时候从Redis返回的是以前老的数据

Redis复制如何处理Key的过期

Slave不会让Key过期,而是等待Master让Key过期。当一个Master让一个Key到期时,它会合成一个DEL命令并传输到所有的Slave。包括源码中也体现了非主库不删:

int expireIfNeeded(redisDb *db, robj *key) { 
 time_t when = getExpire(db,key); 
 
 if (when < 0) return 0; /* No expire for this key */ 
 
 /* Don't expire anything while loading. It will be done later. */ 
 if (server.loading) return 0; 
 
 /* If we are running in the context of a slave, return ASAP: 
 * the slave key expiration is controlled by the master that will 
 * send us synthesized DEL operations for expired keys. 
 * 
 * Still we try to return the right information to the caller, 
 * that is, 0 if we think the key should be still valid, 1 if 
 * we think the key is expired at this time. */ 
 if (server.masterhost != NULL) { 
 return time(NULL) > when; 
 } 
 
 /* Return when this key has not expired */ 
 if (time(NULL) <= when) return 0; 
 
 /* Delete the key */ 
 server.stat_expiredkeys++; 
 propagateExpire(db,key); 
 return dbDelete(db,key); 
}

无需磁盘参与的复制

正常情况下,一个全量重同步要求在磁盘上创建一个RDB文件,然后将它从磁盘加载进内存,然后Slave以此进行数据同步。

如果磁盘性能很低的话,这对Master是一个压力很大的操作。Redis2.8.12是第一个支持无磁盘复制的版本。在此设置中,子进程直接发送RDB文件给Slave,无需使用磁盘作为中间存储介质。


# 默认是关闭的,使用的时候将 no 改为 yes 即可。
# 最终都会把RBD快照文件发送给丛节点,开启以后会不写入磁盘直接发送,关闭以后先写入磁盘再发送快照,默认关闭。
repl-diskless-sync no

间歇性凌云壮志,持续性混吃等死