【Redis学习笔记】13.Redis 主从复制

Redis 主从复制

  • 1. Redis 主从复制特性
  • 2. Redis 主从复制流程
  • 3. Redis 主从复制操作
    • 3.1. 快速部署Redis实例
    • 3.2. 配置主从复制
    • 3.3. 查看主从复制
    • 3.4. 主从复制测试
  • 4. Redis 主从复制危险操作
    • 4.1. 使用热更新配置误操作
    • 4.2. 避免热更新配置误操作
  • 5. 模拟 Redis 主从复制错误数据恢复
    • 5.1. 清空数据
    • 5.2. 在主库批量创建数据并备份
    • 5.3. 同步从库的数据造成数据丢失
    • 5.4. 恢复主库的数据
  • 6. 模拟线上环境主库故障恢复
    • 6.1. 配置主从模拟线上环境
    • 6.2. 模拟主库宕机验证从库是否可读写
    • 6.3. 关闭从库的主从复制保证业务的不间断
    • 6.4. 修复故障的主库同步原来从库的数据
    • 6.5. 从库重新上线为主库
    • 6.6. 将应用的redis地址再次修改为主库的地址
  • 7. Redis 主从复制注意

1. Redis 主从复制特性

1. Redis 主从是异步复制
2. 一个主库可以有多个从库
3. 从库也可以有自己的从库
4. 复制功能不会阻塞主库
5. 可以通过复制功能来让主库免于执行持久化操作,由从库去执行持久化操作即可,但主库还是建议打开,防止雪崩灾难导致数据丢失

2. Redis 主从复制流程

1. 从节点发送同步请求到主节点
2. 主节点接收到从库的请求之后,做如下操作:
	- 立即执行 bgsave 将当前内存里的数据持久化到磁盘
	- 持久化完成之后,将 rdb 文件发送给从节点
3. 从节点从主节点接收到 rdb 文件之后,做了如下操作:
	- 清空自己的数据
	- 载入从主节点接收的 rdb 文件到自己的内存中

3. Redis 主从复制操作

IP地址 端口 角色
192.168.66.51 6381 主库
192.168.66.51 6382 从库

3.1. 快速部署Redis实例

# 下载软件包
cd /usr/loca/src/
wget https://download.redis.io/releases/redis-5.0.7.tar.gz
# 解压软件包
tar xvf redis-5.0.7.tar.gz
cd redis-5.0.7
# 编译安装
make PREFIX=/usr/local/redis install
# 设置环境变量
export PATH="/usr/local/redis/bin:$PATH"
echo 'export PATH="/usr/local/redis/bin:$PATH"' >> /etc/profile

# 创建相关目录
mkdir -p /opt/redis/{6381,6382}/{etc,logs,pid} /data/redis/{6381,6382}

# 创建6381配置文件
REDIS_PORT=6381
cat > /opt/redis/6381/etc/6381.conf << EOF
daemonize yes
bind 127.0.0.1 $(ip a ls eth0|awk -F'[ /]+' 'NR==3{print $3}')
port ${REDIS_PORT}
pidfile /opt/redis/${REDIS_PORT}/pid/${REDIS_PORT}.pid
logfile /opt/redis/${REDIS_PORT}/logs/${REDIS_PORT}.log
dir "/data/redis/${REDIS_PORT}"
dbfilename "${REDIS_PORT}.rdb"
appendonly yes
appendfilename "${REDIS_PORT}.aof"
appendfsync everysec
EOF

cat > /usr/lib/systemd/system/redis-6381.service << EOF
[Unit]
Description=Redis persistent key-value database
After=network.target
After=network-online.target
Wants=network-online.target

[Service]
ExecStart=/usr/local/redis/bin/redis-server /opt/redis/${REDIS_PORT}/etc/${REDIS_PORT}.conf --supervised systemd
ExecStop=/usr/local/redis/bin/redis-shutdown ${REDIS_PORT}
Type=notify
User=redis
Group=redis
RuntimeDirectory=redis
RuntimeDirectoryMode=0755

[Install]
WantedBy=multi-user.target
EOF

# 创建6382配置文件
REDIS_PORT=6382
cat > /opt/redis/6382/etc/6382.conf << EOF
daemonize yes
bind 127.0.0.1 $(ip a ls eth0|awk -F'[ /]+' 'NR==3{print $3}')
port ${REDIS_PORT}
pidfile /opt/redis/${REDIS_PORT}/pid/${REDIS_PORT}.pid
logfile /opt/redis/${REDIS_PORT}/logs/${REDIS_PORT}.log
dir "/data/redis/${REDIS_PORT}"
dbfilename "${REDIS_PORT}.rdb"
appendonly yes
appendfilename "${REDIS_PORT}.aof"
appendfsync everysec
EOF

cat > /usr/lib/systemd/system/redis-6382.service << EOF
[Unit]
Description=Redis persistent key-value database
After=network.target
After=network-online.target
Wants=network-online.target

[Service]
ExecStart=/usr/local/redis/bin/redis-server /opt/redis/${REDIS_PORT}/etc/${REDIS_PORT}.conf --supervised systemd
ExecStop=/usr/local/redis/bin/redis-shutdown ${REDIS_PORT}
Type=notify
User=redis
Group=redis
RuntimeDirectory=redis
RuntimeDirectoryMode=0755

[Install]
WantedBy=multi-user.target
EOF

# 创建普通用户
groupadd -g 2003 redis
useradd -u 2003 -g redis -s /sbin/nologin -M -c "Redis Service User" redis

# 目录授权
chown -R redis. /opt/redis/
chown -R redis. /data/redis/

# 创建redis停止脚本
cat > /usr/local/redis/bin/redis-shutdown << 'EOF'
#!/bin/bash
#
# Wrapper to close properly redis and sentinel
test x"$REDIS_DEBUG" != x && set -x

REDIS_CLI=/usr/local/redis/bin/redis-cli


# Retrieve service name
SERVICE_NAME="$1"


if [ -z "$SERVICE_NAME" ]; then
    SERVICE_NAME=redis
else
    REDIS_DIR="/opt/redis/${SERVICE_NAME}"
fi

# Get the proper config file based on service name
CONFIG_FILE="${REDIS_DIR}/etc/$SERVICE_NAME.conf"


# Use awk to retrieve host, port from config file
HOST=`awk '/^[[:blank:]]*bind/ { print $2 }' $CONFIG_FILE | tail -n1`
PORT=`awk '/^[[:blank:]]*port/ { print $2 }' $CONFIG_FILE | tail -n1`
PASS=`awk '/^[[:blank:]]*requirepass/ { print $2 }' $CONFIG_FILE | tail -n1`
SOCK=`awk '/^[[:blank:]]*unixsocket\s/ { print $2 }' $CONFIG_FILE | tail -n1`

# Just in case, use default host, port
HOST=${HOST:-127.0.0.1}
if [ "$SERVICE_NAME" = redis ]; then
    PORT=${PORT:-6379}
else
    PORT=${PORT:-26739}
fi

# Setup additional parameters
# e.g password-protected redis instances
[ -z "$PASS"  ] || ADDITIONAL_PARAMS="-a $PASS"

# shutdown the service properly
if [ -e "$SOCK" ] ; then
    $REDIS_CLI -s $SOCK $ADDITIONAL_PARAMS shutdown
else
    $REDIS_CLI -h $HOST -p $PORT $ADDITIONAL_PARAMS shutdown
fi
EOF
chmod +x /usr/local/redis/bin/redis-shutdown

# 启动服务
systemctl daemon-reload
systemctl start redis-6381.service
systemctl start redis-6382.service
netstat -nlutp|grep 638

3.2. 配置主从复制

方法1:临时配置,重启服务失效

redis-cli -p 6382 SLAVEOF 192.168.66.51 6381

方法2:写入配置文件,需要重启服务升序

SLAVEOF 192.168.66.51 6381

可以同时使用这两种方法,这样就不需要重启服务了

提示:如果主库配置了 requirepass 参数,需要在配置主从是配置文件中配置 masterauth 参数,如果在命令行中执行 CONFIG SET masterauth

3.3. 查看主从复制

# 查看主库
~]# redis-cli -p 6381 info replication
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=6382,state=online,offset=168,lag=1
master_replid:6a390e04c12fd6e40540c4c8e173b28b6312fb04
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:168
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:168

# 查看从库
~]# redis-cli -p 6382 info replication
# Replication
role:slave
master_host:192.168.66.51
master_port:6381
master_link_status:up
master_last_io_seconds_ago:7
master_sync_in_progress:0
slave_repl_offset:98
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:6a390e04c12fd6e40540c4c8e173b28b6312fb04
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:98
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:98

3.4. 主从复制测试

# 1. 在主库中写入测试数据
~]# for i in {0..2000}; do redis-cli -p 6381 set k_${i} v_${i}; echo "k_${i} is ok"; done
# 2. 在从库上查看数据
~]# redis-cli -p 6381 dbsize
(integer) 2001
~]# redis-cli -p 6382 dbsize
(integer) 2001

4. Redis 主从复制危险操作

4.1. 使用热更新配置误操作

Redis 主从复制如果使用热更新配置,有时候会因为选错主机把从库误认为主库,结果在主库上执行了 SLAVEOF,这样就会导致主库上的数据被清空,因为从库上是没有数据的

从库在同步主库的时候会把原本自己的数据全部清空

误操作过程

# 从库数据为0
127.0.0.1:6382> DBSIZE
(integer) 0
# 主库数据为2001
127.0.0.1:6381> DBSIZE
(integer) 2001

# 在主库上操作同步本该从库的数据
127.0.0.1:6381> SLAVEOF 192.168.66.51 6382
OK

# 再次查看数据,数据已经清空
127.0.0.1:6381> DBSIZE
(integer) 0

4.2. 避免热更新配置误操作

  1. 不使用热更新,直接在配置文件里配置主从
  2. 在执行slaveof的时候先执行bgsave,把数据手动备份一下,然后在数据目录,将rdb文件复制成另一个文件,做备份,这样即使出现问题也能即使恢复。bgsave之后不用重启,直接备份rdb文件即可
# 1. 手动持久化
127.0.0.1:6381> BGSAVE
Background saving started

# 2. 备份rdb文件
~]# cd /data/redis/6381/
6381]# cp 6381.rdb 6381.rdb.bak

# 3. 再次同步错误的主库,造成数据丢失
127.0.0.1:6381> SLAVEOF 192.168.66.51 6382
OK
127.0.0.1:6381> keys *
(empty list or set)

# 4. 还原备份的 rdb 文件,先停掉 Redis,在还原
~]# redis-cli shutdown
6381]# cp 6381.rdb.bak 6381.rdb

# 5. 查看还原后的数据
~]# systemctl start redis-6381.service
~]# redis-cli -p 6381 dbsize
(integer) 2001

5. 模拟 Redis 主从复制错误数据恢复

模拟思路:

1. 清空两个 Redis 的数据
​2. 在主库上创建一些数据,然后使用 bgsave 命令,将数据保存到磁盘,再将磁盘的rdb文件备份
3. 再将从库的数据同步过来,模拟主库数据丢失
4. 从rdb备份文件还原数据库
这个实验的主要的目的是操作redis备份还原

5.1. 清空数据

先关闭再删除数据再启动

[root@redis-1 ~]# redis-cli shutdown
[root@redis-1 ~]# rm -rf /data/redis_cluster/redis_6379/data/*
[root@redis-1 ~]# redis-server /data/redis_cluster/redis_6379/conf/redis_6379.conf 

[root@redis-1 ~]# redis-cli
127.0.0.1:6379> keys *
(empty list or set)

5.2. 在主库批量创建数据并备份

1.批量创建数据
[root@redis-1 ~]# for i in {0..2000}; do redis-cli set k_${i} v_${i}; echo "k_${i} is ok"; done
127.0.0.1:6379> DBSIZE
(integer) 2001

2.将近数据写入到
127.0.0.1:6379> BGSAVE
Background saving started

3.备份rdb文件
[root@redis-1 /data/redis_cluster/redis_6379/data]# cp redis_6379.rdb redis_6379.rdb.bak
[root@redis-1 /data/redis_cluster/redis_6379/data]# ll
总用量 56
-rw-r--r--. 1 root root 27877 128 21:00 redis_6379.rdb
-rw-r--r--. 1 root root 27877 128 21:01 redis_6379.rdb.bak

5.3. 同步从库的数据造成数据丢失

这时从库的数据应该是空的

[root@redis-2 ~]# redis-cli 
127.0.0.1:6379> keys *
(empty list or set)

主库同步从库的数据,同步完主库数据丢失,这样就模拟了主库数据丢失的情况

127.0.0.1:6379> SLAVEOF 192.168.81.220 6379
OK
127.0.0.1:6379> keys *
(empty list or set)

5.4. 恢复主库的数据

先关掉redis,再还原,最后在开启redis

1.关掉redis
[root@redis-1 ~]# redis-cli shutdown

2.还原rdb备份文件
[root@redis-1 /data/redis_cluster/redis_6379/data]# cp redis_6379.rdb.bak redis_6379.rdb
cp:是否覆盖"redis_6379.rdb"? y

3.启动redis
[root@redis-1 ~]# redis-server /data/redis_cluster/redis_6379/conf/redis_6379.conf 

4.查看数据是否还原
[root@redis-1 ~]# redis-cli 
127.0.0.1:6379> keys *

6. 模拟线上环境主库故障恢复

模拟思路:

1. 首先保障主从同步已经配置完成,主库(6381)和从库(6382)都有数据
2. 关闭 Redis 主库(6381),模拟 Redis 主库(6381)宕机
3. 查看 Redis 从库(6382)是否存在数据,是否是只读不能写
4. 关闭从库(6382) slaveof,停止主从同步,将应用连接的 Redis 地址改为从库的,保证业务不断
5. 修复主库(6381),将主库(6381)上线后,与从库(6382)建立新的主从复制关系,旧从库(6382)就变成了新的主库,旧主库(6381)变成新从库,这时关闭应用程序,停止数据写入
6. 然后将新主库(6382)的数据复制到新从库(6381)
7. 关闭新从库(6381)的slaveof,停止主从复制,让后将新主库(6382)配置slaveof
8. 数据复制完成后,故障恢复就完成

简单来说:主库因为某种原因宕机无法提供服务了,直接将从库切换成主库提供服务,然后后原来的主库恢复后同步当前主库的数据,然后停掉所有线上运行的程序,将现在的主库去同步恢复后的主库,重新生成主从关系。

6.1. 配置主从模拟线上环境

配置主从前先保证主库上面有数据

1.登陆主库redis-1查看是否有数据
[root@redis-1 ~]# redis-cli
127.0.0.1:6379> DBSIZE
(integer) 2001

2.登陆从库redis-2查看是否有数据
[root@redis-2 ~]# redis-cli 
127.0.0.1:6379> keys *
(empty list or set)

3.从库没有数据的情况下在从库上配置主从,同步主库的数据
127.0.0.1:6379> SLAVEOF 192.168.81.210 6379
OK
数据已经同步
127.0.0.1:6379> DBSIZE 
(integer) 2001

6.2. 模拟主库宕机验证从库是否可读写

1.直接关掉主库,造成宕机
[root@redis-1 ~]# redis-cli shutdown

2.查看从库是否可读写
只能读,不能写
[root@redis-2 ~]# redis-cli get k_1
"v_1"
[root@redis-2 ~]# redis-cli set k999 k_1
(error) READONLY You can't write against a read only slave.

主库一宕机,从库就会一直输出日志提示连接不上主库

6.3. 关闭从库的主从复制保证业务的不间断

现在主库是不可用的,从库也只能读不能写,但是数据只有这么一份了,我们只能关闭从库上的主从复制,让从库变成主库,再配置业务的redis地址,首先要保证业务的不中断

1.关闭从库redis-2的主从同步配置,使其成为主库
[root@redis-2 ~]# redis-cli slaveof no one
OK

2.将应用的redis地址修改为从库,只要从库关掉了主从配置,他自己就是一个可读可写的库了,库里有故障前的所有数据,可以先保证业务的不间断

6.4. 修复故障的主库同步原来从库的数据

修复完主库,已经是有数据的了,为什么还要同步从库的数据呢,因为在主库挂掉的那一瞬间,从库去掉了主从配置,自己已经成了主库,并且也提供了一段时间的数据写入,这时从库的数据时最完整的

同步现在主库(原来的从库)的数据时,先要将应用关掉,不要在往里写数据了

在主库(原来的从库上)写入几个新的数据,模拟产生的新数据

[root@redis-2 ~]# redis-cli 
127.0.0.1:6379> set zuixinshujv vvvvv
OK

在重新上线的主库上配置主从同步,使自己变成从库,同步主库的(原来的从库)数据

同步之后,现在从库(重新修复的主库已经有最新数据了)

同步之前先将应用停掉,不要再往redis中写数据
[root@redis-1 ~]# redis-cli 
127.0.0.1:6379> SLAVEOF 192.168.81.220 6379
OK
127.0.0.1:6379> get zuixinshujv
"vvvvv"

6.5. 从库重新上线为主库

这里的从库重新上线指的就是原来故障的主库,现在已经同步到最新数据了,因此要上线成为主库,之前选他作为主库就是因为他的性能比从库各方面都要高,避免将来因为性能再次发生故障,因此要切换

1.关闭从库(原来的主库)的主从配置
[root@redis-1 ~]# redis-cli 
127.0.0.1:6379> SLAVEOF no one
OK

2.在主库(原来的从库)配置主从复制
[root@redis-2 ~]# redis-cli 
127.0.0.1:6379> SLAVEOF 192.168.81.210 6379
OK

6.6. 将应用的redis地址再次修改为主库的地址

目前主库已经恢复了,并且主从之前重新建立了主从同步关系,现在就可以把应用的redis地址修改为主库,启动应用就可以了

7. Redis 主从复制注意

1. 从库只读不可写
127.0.0.1:6382> set k1 v1
(error) READONLY You can't write against a read only replica.
2. 从库不会自动故障转移,他只会一直同步主节点
3. 主从复制故障转移需要人为介入
	- 修改代码指向新主的IP地址
	- 从节点需要执行 SLAVEOF no one
4. 从库会清空自己原有的数据,如果同步的对象写错了,就会导致数据丢失
5. 从库和主库后续的同步依靠的是 Redis 的 SYNC 协议,而不是 RDB 文件,RDB 文件只有第一次建立同步时使用
6. 从库也可以正常的持久化文件

无论是同步,无论是主库还是从库操作前,都请先备份一下数据

你可能感兴趣的:(#,Redis,redis,学习,数据库)