blog-frontend icon indicating copy to clipboard operation
blog-frontend copied to clipboard

Redis一主二从三哨兵搭建以及相关原理

Open Caaalabash opened this issue 4 years ago • 0 comments

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-slave1redis-slave2中选出一个作为新的redis-master-new,当旧的redis-master-old复活后, 会成为redis-master-new的slave,如果不是这样,请仔细检查端口

2. 主从复制的实现原理

Redis的主从复制大体上分为三个阶段:建立连接、数据同步、命令传播

2.1 建立连接

  1. slave执行replicaof命令,slave内部的定时任务发现有master信息,开始用socket连接master
  2. 连接建立成功后,slave向master发送ping命令,确定master是否可用,master回复pong则可用,否则slave将断开并重连
  3. 如果master有密码,需要进行身份验证
  4. 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)

第一次连接时,完整重同步步骤

  1. slave向master发送PSYNC <runid> <offset>命令,但第一次连接尚不清楚master的运行id和复制偏移量(offset),因此发送psync-1 -1
  2. master发送FULLRESYNC <runid> <offset>,并开始执行bgsave命令生成RDB文件,使用缓冲区记录此后的所有写命令
  3. bgsave执行完后,向所有服务器发送快照,发送期间继续记录写命令
  4. slave收到快照后丢弃旧数据,载入快照
  5. master快照发送完毕后开始发送缓冲区的写命令
  6. slave开始执行缓冲区写命令

部分重同步的步骤

  1. slave向master发送PSYNC <runid> <offset>命令
  2. 如果runid不正确,或者master的复制积压缓冲区没有足够的命令记录,进行完整重同步,否则只发送slave所却的增量部分
  3. 如果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"]


Caaalabash avatar May 02 '20 08:05 Caaalabash