blog-frontend
blog-frontend copied to clipboard
Redis一主二从三哨兵搭建以及相关原理
1. 实践:Redis搭建1主2从3哨兵
前置条件:三台安装了docker的云服务器
- 2c4g (主), 首先启动redis-master
- 1c2g (从), 启动redis-slave1
- 1c2g (从), 启动redis-slave2
redis配置均在redis官方提供的配置文件样例上做最小修改,实践角度不考虑安全性等问题, 实践证明:安全组开放端口以及不去调整默认端口能让你很快的搭建完毕
// Redis配置
wget http://download.redis.io/redis-stable/redis.conf
// Sentinel配置
wget http://download.redis.io/redis-stable/sentinel.conf
docker启动redis的脚本内容
docker run -d -p 6379:6379 \
-v $PWD/data:/data \
-v $PWD/redis.conf:/etc/redis/redis.conf \
--name [容器名] redis redis-server /etc/redis/redis.conf
docker启动sentinel的脚本内容
docker run -d -p 26379:26379 \
-v $PWD/sentinel.conf:/usr/local/etc/redis/sentinel.conf \
--name [容器名] redis redis-sentinel /usr/local/etc/redis/sentinel.conf
1.1 启动master
调整如下配置:
# 注释这一行,表示Redis可以接受任意ip的连接
# bind 127.0.0.1
# 关闭保护模式
protected-mode no
执行启动脚本后进入容器检查, role为master即可:
docker exec -it redis redis-cli
> 127.0.0.1:6379: info Replication
# Replication
role:master
1.2 启动slave
相对于master而言,只需要多设置一项:将replicaof
设置为master的ip和端口
# replica(复制品) replication(复制)
# replicaof no one会关闭当前服务器的复制并转变为主服务器
# replicaof hostname port会将当前服务器转变为某一服务器的副本服务器
replicaof xx.xx.xx.xxx 6379
执行启动脚本后进入容器检查, role为slave即可:
docker exec -it redis redis-cli
> 127.0.0.1:6379: info Replication
# Replication
role:slave
且日志中出现:
* Connecting to MASTER xx.xx.xx.xxx:6379
* MASTER <-> REPLICA sync started
1.3 验证主从
在master中写入一个key
set howareyou iamfinethankyou
查看slave的日志:
1:S 22 Mar 2020 04:10:49.693 * Connecting to MASTER xx.xx.xx.xxx:6379
1:S 22 Mar 2020 04:10:49.693 * MASTER <-> REPLICA sync started
1:S 22 Mar 2020 04:10:49.727 * Non blocking connect for SYNC fired the event.
1:S 22 Mar 2020 04:10:49.759 * Master replied to PING, replication can continue...
1:S 22 Mar 2020 04:10:49.822 * Partial resynchronization not possible (no cached master)
1:S 22 Mar 2020 04:10:49.855 * Full resync from master: 412d37bf4ebb8e429b56f561d121a43b23ac32a0:0
1:S 22 Mar 2020 04:10:49.913 * MASTER <-> REPLICA sync: receiving 207 bytes from master
1:S 22 Mar 2020 04:10:49.914 * MASTER <-> REPLICA sync: Flushing old data
1:S 22 Mar 2020 04:10:49.914 * MASTER <-> REPLICA sync: Loading DB in memory
1:S 22 Mar 2020 04:10:49.914 * MASTER <-> REPLICA sync: Finished with success
且两个slave的中均能找到howareyou
1.4 添加哨兵
调整如下配置:
# 修改监控的主redis服务器
sentinel monitor mymaster xx.xx.xx.xxx 6379 2
# 在三个机器无法通过内网链接时此项尤为重要
sentinel announce-ip 当前机器的外网ip
在三个机器上使用同样的配置启动sentinel即可
1.5 验证哨兵
停止redis-master
, 会从redis-slave1
和redis-slave2
中选出一个作为新的redis-master-new
,当旧的redis-master-old
复活后,
会成为redis-master-new
的slave,如果不是这样,请仔细检查端口
2. 主从复制的实现原理
Redis的主从复制大体上分为三个阶段:建立连接、数据同步、命令传播
2.1 建立连接
- slave执行replicaof命令,slave内部的定时任务发现有master信息,开始用socket连接master
- 连接建立成功后,slave向master发送ping命令,确定master是否可用,master回复pong则可用,否则slave将断开并重连
- 如果master有密码,需要进行身份验证
- slave发送自己的监听端口,master保存
2.2 数据同步
前置知识:
-
runid
: master的运行id,Redis在启动时随即生成一个长度40的唯一字符串来标识当前节点 -
offset
: 复制偏移量,master和slave各自维护一个复制偏移量,记录传输的字节,当master发送N字节,master.offset += N
, 当slave收到N字节,slave.offset += N
-
repl-backlog-size
: 复制积压缓冲区,由master维护的唯一队列(默认1MB),用于备份最近主库发送给从库的数据
建立连接后,slave向master发送PSYNC命令,执行同步操作,将自身的数据状态更新至master的数据状态,同步分为两种:
- 完整重同步(full resynchronization),两种情况下会是完整重同步
- slave连接上master第一次复制的时候,完整重同步
- 断线重联时,可能是完整重同步
- 部分重同步 (partial resynchronization)
第一次连接时,完整重同步步骤
- slave向master发送
PSYNC <runid> <offset>
命令,但第一次连接尚不清楚master的运行id和复制偏移量(offset),因此发送psync-1 -1
- master发送
FULLRESYNC <runid> <offset>
,并开始执行bgsave
命令生成RDB文件,使用缓冲区记录此后的所有写命令 -
bgsave
执行完后,向所有服务器发送快照,发送期间继续记录写命令 - slave收到快照后丢弃旧数据,载入快照
- master快照发送完毕后开始发送缓冲区的写命令
- slave开始执行缓冲区写命令
部分重同步的步骤
- slave向master发送
PSYNC <runid> <offset>
命令 - 如果runid不正确,或者master的复制积压缓冲区没有足够的命令记录,进行完整重同步,否则只发送slave所却的增量部分
- 如果offset之后的数据在缓冲区中,就发送continue响应,表示可以进行部分复制
最终流程
2.3 命令传播
数据同步后,slave和master的数据暂时达到一致的状态,为了维持数据一致性,master会对slave执行命令传播操作,即每执行一个命令就会向slave发送同样的写命令
同时,slave和master将维持长连接并彼此发送心跳命令
-
master每隔一定时间对从节点发送ping命令
-
slave每隔一定时间发送
REPLCONF ACK <replication_offset>
命令,上报当前的复制偏移量
单机redis多实例配置
version: "3"
services:
master:
image: redis
container_name: redis_master
ports:
- "9000:9000"
volumes:
- $PWD/master.conf:/etc/redis/redis.conf
- $PWD/master-data:/data
restart: unless-stopped
extra_hosts:
- "dockerhost:172.18.0.1"
command: ["redis-server", "/etc/redis/redis.conf"]
slave1:
image: redis
container_name: redis_slave1
ports:
- "9001:9001"
volumes:
- $PWD/slave1.conf:/etc/redis/redis.conf
- $PWD/slave1-data:/data
restart: unless-stopped
extra_hosts:
- "dockerhost:172.18.0.1"
command: ["redis-server", "/etc/redis/redis.conf"]
slave2:
image: redis
container_name: redis_slave2
ports:
- "9002:9002"
volumes:
- $PWD/slave2.conf:/etc/redis/redis.conf
- $PWD/slave2-data:/data
restart: unless-stopped
extra_hosts:
- "dockerhost:172.18.0.1"
command: ["redis-server", "/etc/redis/redis.conf"]