带你走进redis安装、哨兵与集群配置

安装

Redis是REmote DIctionary Server(远程字典服务器)的缩写。一个开源的、高性能的、基于键值对的缓存与存储系统,目前redis稳定版是5.0.8,下载安装:

1
2
3
4
5
6
7
8
cd /usr/local/src/
wget http://download.redis.io/redis-stable.tar.gz
tar zxvf redis-stable.tar.gz
cd redis-stable/
make
make install PREFIX=/usr/local/redis
echo "export PATH=/usr/local/redis/bin:\$PATH" >/etc/profile.d/redis.sh
source /etc/profile.d/redis.sh

在实际安装中,可以使用 make test 命令来测试redis是否编译正常。安装完成后,在/usr/local/redis/bin/目录下面有以下文件

1
2
3
4
5
6
7
8
[root@node2 redis-stable]# ll /usr/local/redis/bin/
total 32772
-rwxr-xr-x 1 root root 4366824 Mar 15 10:36 redis-benchmark #Redis性能测试工具
-rwxr-xr-x 1 root root 8125016 Mar 15 10:36 redis-check-aof #AOF文件修复工具
-rwxr-xr-x 1 root root 8125016 Mar 15 10:36 redis-check-rdb #RDB文件检查工具
-rwxr-xr-x 1 root root 4807808 Mar 15 10:36 redis-cli #Redis命令行客户端
lrwxrwxrwx 1 root root 12 Mar 15 10:36 redis-sentinel -> redis-server #Sentinel服务器
-rwxr-xr-x 1 root root 8125016 Mar 15 10:36 redis-server #Redis服务器

配置

单机版

如果在一台机器上面运行一个redis,是可以使用以下方法。

redis.conf修改配置如下:

1
2
3
4
5
6
7
# /usr/local/src/redis-stable/在目录下运行:
mkdir /usr/local/redis/etc
cp redis.conf /usr/local/redis/etc/
cd /usr/local/redis/etc/
sed -i "s/^daemonize no/daemonize yes/" redis.conf #启用守护进程后,Redis会把pid写到一个pidfile中,在/var/run/redis_6379.pid
sed -i 's/# requirepass.*/requirepass 123456/' redis.conf #修改密码
sed -i 's/bind 127.0.0.1/bind 192.168.1.62/' redis.conf #修改监听IP

dir默认值为dir ./,会保存到/usr/local/redis/目录下。

启动redis以及验证服务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@node2 etc]# /usr/local/redis/bin/redis-server /usr/local/redis/etc/redis.conf
14398:C 15 Mar 2020 10:48:31.325 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
14398:C 15 Mar 2020 10:48:31.325 # Redis version=5.0.8, bits=64, commit=00000000, modified=0, pid=14398, just started
14398:C 15 Mar 2020 10:48:31.325 # Configuration loaded

# 验证服务是否正常
[root@node2 etc]# /usr/local/redis/bin/redis-cli -h 192.168.1.62 -p 6379
192.168.1.62:6379> auth 123456
OK
192.168.1.62:6379> set name fangdm
OK
192.168.1.62:6379> get name
"fangdm"
192.168.1.62:6379>

# 强行终止redis进程会导致数据丢失,因此使用redis-cli shutdown正确停止
[root@node2 etc]# /usr/local/redis/bin/redis-cli -h 192.168.1.62 -a 123456 shutdown
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.

使用systemctl来启动服务。参考:https://gist.github.com/hackedunit/a53f0b5376b3772d278078f686b04d38 ,注意type的区别。

/lib/systemd/system/redis.service
1
2
3
4
5
6
7
8
9
10
11
12
13
[Unit]
Description=Redis persistent key-value storage
After=network.target

[Service]
Type=notify
ExecStart=/usr/local/redis/bin/redis-server /usr/local/redis/etc/redis.conf --supervised systemd --daemonize no
ExecStop=/usr/bin/redis-cli -p 6379 shutdown
ExecReload=/bin/kill -USR2 $MAINPID
Restart=always

[Install]
WantedBy=multi-user.target

这样就可以启动服务了,设置一下自启:

1
2
3
systemctl daemon-reload
systemctl enable redis
systemctl status redis

注意:

  • 如果redis设置了密码,就不能stop了,需要加上密码才能正常stop。或者使用killproc方式来停止。
  • centos6系统的话,可以直接复制utils/redis_init_script 这个文件到/etc/init.d下,然后修改一些参数即可使用。

多实例版

如果要在一台机器上面运行多个实例,那使用官方提供的安装配置脚本会比较方便。

在运行完make install之后,不需要去复制配置,直接运行 ./utils/install_server.sh 会自动生成一份redis配置文件,redis启动脚本,配置好日志以及数据路径等,如下:

1
2
3
4
5
6
7
8
#生成启动脚本
REDIS_PORT=6380 \
REDIS_CONFIG_FILE=/usr/local/redis/etc/redis_${REDIS_PORT}.conf \
REDIS_LOG_FILE=/usr/local/redis/logs/redis_${REDIS_PORT}.log \
REDIS_DATA_DIR=/usr/local/redis/db/${REDIS_PORT} \
REDIS_EXECUTABLE=`command -v redis-server` ./utils/install_server.sh
#运行完脚本,会自动启动redis
/etc/init.d/redis_6380 stop

或者也可以交互式运行生成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
[root@node2 ~]# /usr/local/src/redis-stable/utils
[root@node2 utils]# sh install_server.sh
Welcome to the redis service installer
This script will help you easily set up a running redis server

Please select the redis port for this instance: [6379]
Selecting default: 6379
Please select the redis config file name [/etc/redis/6379.conf] /usr/local/redis/etc/redis.conf
Please select the redis log file name [/var/log/redis_6379.log] /usr/local/redis/logs/redis_6379.log
Please select the data directory for this instance [/var/lib/redis/6379] /usr/local/redis/db/6379
Please select the redis executable path [/usr/local/redis/bin/redis-server] /usr/local/redis/bin/redis-server
Selected config:
Port : 6379
Config file : /usr/local/redis/etc/redis.conf
Log file : /usr/local/redis/logs/redis_6379.log
Data dir : /usr/local/redis/db/6379
Executable : /usr/local/redis/bin/redis-server
Cli Executable : /usr/local/redis/bin/redis-cli
Is this ok? Then press ENTER to go on or Ctrl-C to abort.
Copied /tmp/6379.conf => /etc/init.d/redis_6379
Installing service...
Successfully added to chkconfig!
Successfully added to runlevels 345!
Starting Redis server...
Installation successful!

这样生成的启动的配置文件为:

/etc/init.d/redis_6380
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#!/bin/sh
#Configurations injected by install_server below....

EXEC=/usr/local/redis/bin/redis-server
CLIEXEC=/usr/local/redis/bin/redis-cli
PIDFILE=/var/run/redis_6380.pid
CONF="/usr/local/redis/etc/redis_6380.conf"
REDISPORT="6380"
###############
# SysV Init Information
# chkconfig: - 58 74
# description: redis_6380 is the redis daemon.
### BEGIN INIT INFO
# Provides: redis_6380
# Required-Start: $network $local_fs $remote_fs
# Required-Stop: $network $local_fs $remote_fs
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Should-Start: $syslog $named
# Should-Stop: $syslog $named
# Short-Description: start and stop redis_6380
# Description: Redis daemon
### END INIT INFO


case "$1" in
start)
if [ -f $PIDFILE ]
then
echo "$PIDFILE exists, process is already running or crashed"
else
echo "Starting Redis server..."
$EXEC $CONF
fi
;;
stop)
if [ ! -f $PIDFILE ]
then
echo "$PIDFILE does not exist, process is not running"
else
PID=$(cat $PIDFILE)
echo "Stopping ..."
$CLIEXEC -p $REDISPORT shutdown
while [ -x /proc/${PID} ]
do
echo "Waiting for Redis to shutdown ..."
sleep 1
done
echo "Redis stopped"
fi
;;
status)
PID=$(cat $PIDFILE)
if [ ! -x /proc/${PID} ]
then
echo 'Redis is not running'
else
echo "Redis is running ($PID)"
fi
;;
restart)
$0 stop
$0 start
;;
*)
echo "Please use start, stop, restart or status as first argument"
;;
esac

配置文件

redis.conf里面的配置项 https://www.cnblogs.com/linuxk/p/9474928.html ,这一篇文章写得很详细了。以下是我认为比较重要的配置解释:

持久化数据

redis持久化数据有2种方法,AOF以及RDB。

RDB

RDB方式,是将redis某一时刻的数据持久化到磁盘中,是一种快照式的持久化方法。redis在进行数据持久化的过程中,会先将数据写入到一个临时文件中,待持久化过程都结束了,才会用这个临时文件替换上次持久化好的文件。正是这种特性,让我们可以随时来进行备份,因为快照文件总是完整可用的。

对于RDB方式,redis会单独创建(fork)一个子进程来进行持久化,而主进程是不会进行任何IO操作的,这样就确保了redis极高的性能。

如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。虽然RDB有不少优点,但它的缺点也是不容忽视的。如果你对数据的完整性非常敏感,那么RDB方式就不太适合你,因为即使你每5分钟都持久化一次,当redis故障时,仍然会有近5分钟的数据丢失。所以,redis还提供了另一种持久化方式,那就是AOF。

相关的配置有:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 指定在多长时间内,有多少次更新操作,就将数据同步到数据文件,可以多个条件配合
# 满足以下条件将会同步数据:
# 900秒(15分钟)内有1个更改
# 300秒(5分钟)内有10个更改
# 60秒内有10000个更改
# Note: 可以把所有“save”行注释掉,这样就取消同步操作了
save 900 1
save 300 10
save 60 10000

stop-writes-on-bgsave-error yes   #当save过程中出现失败的情况时,或者有某些错误时,总之导致了内存中的数据和磁盘中的数据不一致了。该参数定义此时是否继续进行save的操作。
rdbcompression yes # 指定存储至本地数据库时是否压缩数据,默认为yes,Redis采用LZF压缩
# 如果为了节省CPU时间,可以关闭该选项,但会导致数据库文件变的巨大
rdbchecksum yes # 当save完成后,是否使用CRC64算法校验rdb文件。
dbfilename dump.rdb # 指定本地数据库文件名,默认值为dump.rdb
dir /data/redis_data #数据库(dump.rdb)文件存放目录,注意,这里只能指定一个目录,不能指定文件名

主要定义了dump.rdb是在保存在哪个目录下面,以及需要满足什么条件会再次备份。

AOF

AOF,英文是Append Only File,即只允许追加不允许改写的文件。如前面介绍的,AOF方式是将执行过的写指令记录下来,在数据恢复时按照从前到后的顺序再将指令都执行一遍,就这么简单。

默认的AOF持久化策略是每秒钟fsync一次(fsync是指把缓存中的写指令记录到磁盘中),因为在这种情况下,redis仍然可以保持很好的处理性能,即使redis故障,也只会丢失最近1秒钟的数据。如果在追加日志时,恰好遇到磁盘空间满、inode满或断电等情况导致日志写入不完整,也没有关系,redis提供了redis-check-aof工具,可以用来进行日志修复。相关的配置有:

1
2
3
4
5
6
7
8
9
10
11
appendonly no   #yes表示开启aof持久化。指定是否在每次更新操作后进行日志记录,
#Redis在默认情况下是异步的把数据写入磁盘,如果不开启,可能会在断电时导致一段时间内的数据丢失。
# 因为redis本身同步数据文件是按上面save条件来同步的,
# 所以有的数据会在一段时间内只存在于内存中。默认为no

appendfilename "appendonly.aof" # 指定更新日志文件名,默认为appendonly.aof

appendfsync everysec # 指定更新日志条件,共有3个可选值:
# no:不调用fsync()函数,而是让操作系统自行决定sync的时间,这种模式下,redis的性能会最快。
# always:在每次写请求后都调用fsync()函数,这种模式下,redis相对会较慢,但是数据是最安全的。
# everysec:每秒钟调用一次fsync(),这是性能和安全的折中选择。默认情况下为该模式

持久化套路

根据 https://www.cnblogs.com/rjzheng/p/10990713.html 的经验,一般我们在生产上采用的持久化策略为

  • (1)master关闭持久化
  • (2)slave开RDB即可,必要的时候AOF和RDB都开启

Reids6种淘汰策略

  • noeviction: 不删除策略, 达到最大内存限制时, 如果需要更多内存, 直接返回错误信息。大多数写命令都会导致占用更多的内存(有极少数会例外。
  • allkeys-lru:所有key通用; 优先删除最近最少使用(less recently used ,LRU) 的 key。
  • volatile-lru:只限于设置了 expire 的部分; 优先删除最近最少使用(less recently used ,LRU) 的 key。
  • allkeys-random:所有key通用; 随机删除一部分 key。
  • volatile-random: 只限于设置了 expire 的部分; 随机删除一部分 key。
  • volatile-ttl: 只限于设置了 expire 的部分; 优先删除剩余时间(time to live,TTL) 短的key。

主从配置

使用上文配置好的2个redis实例,即redis_6379以及redis_6380,进行主从的配置。

我们将redis_6379配置为主,redis_6380配置为备。默认情况下,redis就是主的角色,所以redis_6379不需要修改配置。在redis_6380修改配置:

1
2
replicaof 127.0.0.1 6379    #配置为master的从,如果master上有密码配置,还需要增加下面一项密码配置
#masterauth 123456 #配置主的密码

再重启:/etc/init.d/redis_6380 restart,进入redis,使用info replication查看状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
[root@node2 ~]# redis-cli -p 6380
127.0.0.1:6380> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:7
master_sync_in_progress:0
slave_repl_offset:14
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:a0e1754cc1cbc4ef83eea7fafd99e451c05ef987
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:14
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:14
127.0.0.1:6380> keys *
1) "age"
2) "name"
127.0.0.1:6380>

role为slave,master_link_status是up状态的。也可以正常获取到key,说明主从已经配置好了。很简单,redis主从和mysql主从不一样,redis主从不用事先同步数据,它会自动同步过去。

建立复制的配置方式有三种:

  • redis.conf文件中配置replicaof选项,然后指定该配置文件启动Redis生效。
  • redis-server启动命令后加上--replicaof启动生效。即 ./redis-server /etc/redis/6379.conf --replicaof 127.0.0.1 8888
  • 直接使用 replicaof命令在从节点执行生效。

如果想要复制断开,在从节点执行命令REPLICAOF on one来断开于主节点的复制关系。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
127.0.0.1:6380> REPLICAOF no one
OK
127.0.0.1:6380> info replication
# Replication
role:master
connected_slaves:0
master_replid:f72a33374022dba319f97e322430211f1e279188
master_replid2:a0e1754cc1cbc4ef83eea7fafd99e451c05ef987
master_repl_offset:1162
second_repl_offset:1163
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:1162
127.0.0.1:6380>
127.0.0.1:6380>
127.0.0.1:6380> REPLICAOF 127.0.0.1 6379
OK

但是主从是有问题的,当主有异常时,redis没有机制将从升级为主,需要再进行配置才能支持。有2个方法,一是sentinel模式,二是cluster模式。

Redis哨兵模式

Sentinel简介

Redis Sentinel是Redis高可用的实现方案。Sentinel是一个管理多个Redis实例的工具,它可以实现对Redis的监控、通知、自动故障转移。

Sentinel的主要功能包括主节点存活检测、主从运行情况检测、自动故障转移(failover)、主从切换。Redis的Sentinel最小配置是一主一从。Redis的Sentinel系统可以用来管理多个Redis服务器,该系统可以执行以下四个任务:

  • 监控:Sentinel会不断的检查主服务器和从服务器是否正常运行。
  • 通知:当被监控的某个Redis服务器出现问题,Sentinel通过API脚本向管理员或者其他的应用程序发送通知。
  • 自动故障转移:当主节点不能正常工作时,Sentinel会开始一次自动的故障转移操作,它会将与失效主节点是主从关系的其中一个从节点升级为新的主节点, 并且将其他的从节点指向新的主节点。
  • 配置提供者:在Redis Sentinel模式下,客户端应用在初始化时连接的是Sentinel节点集合,从中获取主节点的信息。

Sentinel是Redis的高可用性解决方案:由一个或多个Sentinel实例组成的Sentinel系统可以监视任意多个主服务器,以及所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器,然后由新的主服务器代替已下线的主服务器继续处理命令请求 。如下图:

img

Sentinel负责监控集群中的所有主、从Redis,当发现主故障时,Sentinel会在所有的从中选一个成为新的主。并且会把其余的从变为新主的从。同时那台有问题的旧主也会变为新主的从,也就是说当旧的主即使恢复时,并不会恢复原来的主身份,而是作为新主的一个从。在Redis高可用架构中,Sentinel往往不是只有一个,而是有3个或者以上。目的是为了让其更加可靠,毕竟主和从切换角色这个过程还是蛮复杂的。

  • 主观失效:SDOWN(subjectively down),直接翻译的为”主观”失效,即当前sentinel实例认为某个redis服务为”不可用”状态.
  • 客观失效:ODOWN(objectively down),直接翻译为”客观”失效,即多个sentinel实例都认为master处于”SDOWN”状态,那么此时master将处于ODOWN,ODOWN可以简单理解为master已经被集群确定为”不可用”,将会开启failover

Sentinel部署

准备工作

分别有3个Sentinel节点,1个主节点,2个从节点组成一个Redis Sentinel

role IP port
master 127.0.0.1 6379
slave1 127.0.0.1 6380
slave2 127.0.0.1 6381
Sentinel1 127.0.0.1 26379
Sentinel2 127.0.0.1 26380
Sentinel3 127.0.0.1 26381

使用多实例的方法,配置好主从模式。在主上面运行info replication,查看slave信息:

1
2
3
4
[root@node2 ~]# redis-cli -p 6379 info replication |grep slave
connected_slaves:2
slave0:ip=127.0.0.1,port=6380,state=online,offset=196,lag=0
slave1:ip=127.0.0.1,port=6381,state=online,offset=196,lag=0

Sentinel配置讲解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 端口
port 26379

# 是否后台启动
daemonize yes

# pid文件路径
pidfile /var/run/redis-sentinel.pid

# 日志文件路径
logfile "/var/log/sentinel.log"

# 定义工作目录
dir /tmp

# 定义Redis主的别名, IP, 端口,这里的2指的是需要至少2个Sentinel认为主Redis挂了才最终会采取下一步行为
sentinel monitor mymaster 127.0.0.1 6379 2

# 如果mymaster 30秒内没有响应,则认为其主观失效
sentinel down-after-milliseconds mymaster 30000

# 如果master重新选出来后,其它slave节点能同时并行从新master同步数据的台数有多少个,显然该值越大,所有slave节点完成同步切换的整体速度越快,但如果此时正好有人在访问这些slave,可能造成读取失败,影响面会更广。
# 最保守的设置为1,同一时间,只能有一台干这件事,这样其它slave还能继续服务,但是所有slave全部完成缓存更新同步的进程将变慢。
sentinel parallel-syncs mymaster 1

# 该参数指定一个时间段,在该时间段内没有实现故障转移成功,则会再一次发起故障转移的操作,单位毫秒
sentinel failover-timeout mymaster 180000

# 不允许使用SENTINEL SET设置notification-script和client-reconfig-script。
sentinel deny-scripts-reconfig yes

启动Sentinel

先进入编译目录,然后运行以下命令:

1
2
3
4
5
6
mkdir /usr/local/redis/sentinel/{log,pidfile,dir,etc} -p
for port in 26379 26380 26381;do
mkdir /usr/local/redis/sentinel/dir/sentinel_$port
cat sentinel.conf |grep -v '^#' |grep -v '^$' |sed "s/^port.*/port $port/;s#pidfile.*#pidfile /usr/local/redis/sentinel/pidfile/sentinel_$port.pid#;s#logfile.*#logfile /usr/local/redis/sentinel/log/sentinel_$port.log#;s#dir.*#dir /usr/local/redis/sentinel/dir/sentinel_$port#;s/daemonize.*/daemonize yes/" >/usr/local/redis/sentinel/etc/sentinel_$port.conf
redis-sentinel /usr/local/redis/sentinel/etc/sentinel_$port.conf
done

查看进程,保证进程都有正常启动。

1
2
3
4
[root@node2 log]# ps aux |grep redis-sentinel
root 7873 0.9 0.1 153896 2640 ? Ssl 21:33 0:00 redis-sentinel *:26379 [sentinel]
root 7984 1.0 0.1 153896 2640 ? Ssl 21:33 0:00 redis-sentinel *:26380 [sentinel]
root 7997 1.0 0.1 153896 2560 ? Ssl 21:33 0:00 redis-sentinel *:26381 [sentinel]

查看sentinel信息

登陆sentinel,来查看info信息,可以看到,有启动了一个master0名为mymaster,从节点有2个。

1
2
3
4
5
6
7
8
[root@node2 log]# redis-cli -p 26379 info sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=127.0.0.1:6379,slaves=2,sentinels=3

可以使用 sentinel master mymaster 查看更详细的信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#输出被监控的主节点的状态信息
[root@node2 log]# redis-cli -p 26379 sentinel master mymaster
1) "name"
2) "mymaster"
3) "ip"
4) "127.0.0.1"
5) "port"
6) "6379"
7) "runid"
8) "3ac78c298bb06137afe3b0f3d02a323a12494dfb"
9) "flags"
10) "master"
...
#查看mymaster的从信息,可以看到有2个从节点
[root@node2 log]# redis-cli -p 26379 sentinel slaves mymaster
1) 1) "name"
2) "127.0.0.1:6381"
3) "ip"
4) "127.0.0.1"
5) "port"
6) "6381"
7) "runid"
8) "63d7e25f5fb8d3fedba4c7948b4d3e2bee14b051"
9) "flags"
10) "slave"
...
2) 1) "name"
2) "127.0.0.1:6380"
3) "ip"
4) "127.0.0.1"
5) "port"
6) "6380"
7) "runid"
8) "05746ecbcdd7a84cc1d4cb6db7b9467aa656a6fe"
9) "flags"
10) "slave"
...

sentinel切换

先把主redis停止掉,/etc/init.d/redis_6379 stop,稍等一会,这时可以看到日志:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[root@node2 ~]# tailf /usr/local/redis/sentinel/log/sentinel_26379.log
7873:X 15 Mar 2020 21:47:18.946 # +sdown master mymaster 127.0.0.1 6379
7873:X 15 Mar 2020 21:47:19.031 # +odown master mymaster 127.0.0.1 6379 #quorum 2/2
7873:X 15 Mar 2020 21:47:19.031 # +new-epoch 1
7873:X 15 Mar 2020 21:47:19.031 # +try-failover master mymaster 127.0.0.1 6379
7873:X 15 Mar 2020 21:47:19.044 # +vote-for-leader 358c10fcf21364b2d414df5130c96cda2d624544 1
7873:X 15 Mar 2020 21:47:19.070 # eaddc2a694fd98ce6e331a3096f8852ccdd2927a voted for 358c10fcf21364b2d414df5130c96cda2d624544 1
7873:X 15 Mar 2020 21:47:19.081 # 691f1baf69e51a31967445a1aa5a3b24b5304e71 voted for 358c10fcf21364b2d414df5130c96cda2d624544 1
7873:X 15 Mar 2020 21:47:19.109 # +elected-leader master mymaster 127.0.0.1 6379
7873:X 15 Mar 2020 21:47:19.109 # +failover-state-select-slave master mymaster 127.0.0.1 6379
7873:X 15 Mar 2020 21:47:19.161 # +selected-slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
7873:X 15 Mar 2020 21:47:19.164 * +failover-state-send-slaveof-noone slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
7873:X 15 Mar 2020 21:47:19.241 * +failover-state-wait-promotion slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
7873:X 15 Mar 2020 21:47:19.679 # +promoted-slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
7873:X 15 Mar 2020 21:47:19.680 # +failover-state-reconf-slaves master mymaster 127.0.0.1 6379
7873:X 15 Mar 2020 21:47:19.726 * +slave-reconf-sent slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379
7873:X 15 Mar 2020 21:47:20.232 # -odown master mymaster 127.0.0.1 6379
7873:X 15 Mar 2020 21:47:20.715 * +slave-reconf-inprog slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379
7873:X 15 Mar 2020 21:47:20.715 * +slave-reconf-done slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379
7873:X 15 Mar 2020 21:47:20.777 # +failover-end master mymaster 127.0.0.1 6379
7873:X 15 Mar 2020 21:47:20.777 # +switch-master mymaster 127.0.0.1 6379 127.0.0.1 6380
7873:X 15 Mar 2020 21:47:20.778 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6380
7873:X 15 Mar 2020 21:47:20.778 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6380

可以看到自切到6380上面了。登陆redis_6380查看信息:

1
2
3
4
5
[root@node2 ~]# redis-cli -p 6380 info replication |head -n4
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=6381,state=online,offset=212643,lag=0

如果把redis_6379再启动会发现什么事情呢?运行 /etc/init.d/redis_6379 start 之后,可以发现,redis_6379变成了从。

1
2
3
4
5
6
7
8
9
10
11
[root@node2 ~]# redis-cli -p 6380 info replication |head -n4
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6381,state=online,offset=240792,lag=1
[root@node2 ~]#
[root@node2 ~]# redis-cli -p 6379 info replication |head -n4
# Replication
role:slave
master_host:127.0.0.1
master_port:6380

本小节主要参考文档:

https://blog.csdn.net/men_wen/article/details/72724406

https://www.cnblogs.com/linuxk/p/10718153.html

Redis集群

Redis Cluster简介

Redis Cluster为Redis官方提供的一种分布式集群解决方案。它支持在线节点增加和减少。 集群中的节点角色可能是主,也可能是从,但需要保证每个主节点都要有对应的从节点, 这样保证了其高可用。

Redis Cluster采用了分布式系统的分片(分区)的思路,每个主节点为一个分片,这样也就意味着 存储的数据是分散在所有分片中的。当增加节点或删除主节点时,原存储在某个主节点中的数据 会自动再次分配到其他主节点。

如下图,各节点间是相互通信的,通信端口为各节点Redis服务端口+10000,这个端口是固定的,所以注意防火墙设置, 节点之间通过二进制协议通信,这样的目的是减少带宽消耗。

在Redis Cluster中有一个概念slot,我们翻译为槽。Slot数量是固定的,为16384个。这些slot会均匀地分布到各个 节点上。另外Redis的键和值会根据hash算法存储在对应的slot中。简单讲,对于一个键值对,存的时候在哪里是通过 hash算法算出来的,那么取得时候也会算一下,知道值在哪个slot上。根据slot编号再到对应的节点上去取。

Redis Cluster无法保证数据的强一致性,这是因为当数据存储时,只要在主节点上存好了,就会告诉客户端存好了, 如果等所有从节点上都同步完再跟客户端确认,那么会有很大的延迟,这个对于客户端来讲是无法容忍的。所以, 最终Redis Cluster只好放弃了数据强一致性,而把性能放在了首位。

Redis Cluster搭建

准备节点

Redis 集群一般由多个节点组成,节点数量为6个才能保证组成完整高可用的集群。下面给出一个节点的配置,其他的节点和该节点只是端口不同。整体规划如下:

role IP slave信息
master01 127.0.0.1:6379 127.0.0.1:6382
master02 127.0.0.1:6380 127.0.0.1:6383
master03 127.0.0.1:6381 127.0.0.1:6384

要启用集群,需要开启以下redis配置:

1
2
3
cluster-enabled yes     #开启集群
cluster-config-file nodes-6379.conf #集群的配置文件,首次启动会自动创建
cluster-node-timeout 15000 #集群节点连接超时时间,15秒

按多实例版开启6个节点:

1
2
3
4
5
6
7
8
9
10
11
12
cd /usr/local/src/redis-stable/
for i in `seq 6379 6384`;do
REDIS_PORT=$i \
REDIS_CONFIG_FILE=/usr/local/redis/etc/redis_${REDIS_PORT}.conf \
REDIS_LOG_FILE=/usr/local/redis/logs/redis_${REDIS_PORT}.log \
REDIS_DATA_DIR=/usr/local/redis/db/${REDIS_PORT} \
REDIS_EXECUTABLE=`command -v redis-server` ./utils/install_server.sh
sed -i 's/# cluster-enabled/cluster-enabled/' /usr/local/redis/etc/redis_${i}.conf
sed -i "s/# cluster-config-file.*/cluster-config-file nodes-${i}.conf/" /usr/local/redis/etc/redis_${i}.conf
sed -i 's/# cluster-node-timeout/cluster-node-timeout/' /usr/local/redis/etc/redis_${i}.conf
/etc/init.d/redis_${i} restart
done

有日志文件可得,节点已经启动成功。这个日志文件是Redis服务器普通的日志文件,在集群模式下,第一次也会自动创建一个日志文件,由配置文件cluster-config-file指定文件。

集群配置文件的作用:当集群内节点发生信息变化时,如添加节点、节点下线、故障转移等。节点会自动保存集群的状态到配置文件中。该配置文件由Redis自行维护,不要手动修改,防止节点重启时产生集群信息错乱。

可以查看日志以及使用 CLUSTER NODES 查看现在的状态:

1
2
3
4
5
6
7
8
9
[root@node2 redis]# cat logs/redis_6379.log |tail -n 3
32727:M 15 Mar 2020 23:31:20.136 # Server initialized
32727:M 15 Mar 2020 23:31:20.138 * DB loaded from disk: 0.000 seconds
32727:M 15 Mar 2020 23:31:20.138 * Ready to accept connections
[root@node2 redis]# cat db/6379/nodes-6379.conf
72ae914b9f1068dd2767ff3efd1b76ca149a427f :0@0 myself,master - 0 0 0 connected
vars currentEpoch 0 lastVoteEpoch 0
[root@node2 redis]# redis-cli -p 6379 CLUSTER NODES
72ae914b9f1068dd2767ff3efd1b76ca149a427f :6379@16379 myself,master - 0 0 0 connected

创建集群

下面在任一节点上执行以下构建集群的命令,将这里面的6个节点组建集群模式,--cluster-replicas 1表示每个主对应一个从。使用以下命令会自动实现一主对应一从。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
[root@node2 redis]# redis-cli --cluster create 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384 --cluster-replicas 1
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 127.0.0.1:6383 to 127.0.0.1:6379
Adding replica 127.0.0.1:6384 to 127.0.0.1:6380
Adding replica 127.0.0.1:6382 to 127.0.0.1:6381
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: 72ae914b9f1068dd2767ff3efd1b76ca149a427f 127.0.0.1:6379
slots:[0-5460] (5461 slots) master
M: 2235ce74c983debc10d093d80905630f0c6a2135 127.0.0.1:6380
slots:[5461-10922] (5462 slots) master
M: 2d177d9e09541ac0b0694b82fe60764d3409bbf7 127.0.0.1:6381
slots:[10923-16383] (5461 slots) master
S: 7557022fc35dfc2f34bc4ed40a065e284cbbc7a4 127.0.0.1:6382
replicates 2d177d9e09541ac0b0694b82fe60764d3409bbf7
S: 5731bb3e902d9e7617c6a324b56f2dd2b53d8b80 127.0.0.1:6383
replicates 72ae914b9f1068dd2767ff3efd1b76ca149a427f
S: 680fbb0fb4154e4589b2d7e82a1c0b63adc68f11 127.0.0.1:6384
replicates 2235ce74c983debc10d093d80905630f0c6a2135
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
.
>>> Performing Cluster Check (using node 127.0.0.1:6379)
M: 72ae914b9f1068dd2767ff3efd1b76ca149a427f 127.0.0.1:6379
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: 680fbb0fb4154e4589b2d7e82a1c0b63adc68f11 127.0.0.1:6384
slots: (0 slots) slave
replicates 2235ce74c983debc10d093d80905630f0c6a2135
M: 2d177d9e09541ac0b0694b82fe60764d3409bbf7 127.0.0.1:6381
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
M: 2235ce74c983debc10d093d80905630f0c6a2135 127.0.0.1:6380
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: 5731bb3e902d9e7617c6a324b56f2dd2b53d8b80 127.0.0.1:6383
slots: (0 slots) slave
replicates 72ae914b9f1068dd2767ff3efd1b76ca149a427f
S: 7557022fc35dfc2f34bc4ed40a065e284cbbc7a4 127.0.0.1:6382
slots: (0 slots) slave
replicates 2d177d9e09541ac0b0694b82fe60764d3409bbf7
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

到此提示,集群创建成功!!!!!

查看集群状态

可以使用 CLUSTER INFO 以及 CLUSTER NODES 查看集群的一些信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[root@node2 redis]# redis-cli -p 6381
127.0.0.1:6381> CLUSTER INFO
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:3
cluster_stats_messages_ping_sent:253
cluster_stats_messages_pong_sent:256
cluster_stats_messages_meet_sent:3
cluster_stats_messages_sent:512
cluster_stats_messages_ping_received:254
cluster_stats_messages_pong_received:256
cluster_stats_messages_meet_received:2
cluster_stats_messages_received:512
127.0.0.1:6381> CLUSTER NODES
2235ce74c983debc10d093d80905630f0c6a2135 127.0.0.1:6380@16380 master - 0 1584287363000 2 connected 5461-10922
7557022fc35dfc2f34bc4ed40a065e284cbbc7a4 127.0.0.1:6382@16382 slave 2d177d9e09541ac0b0694b82fe60764d3409bbf7 0 1584287365384 3 connected
680fbb0fb4154e4589b2d7e82a1c0b63adc68f11 127.0.0.1:6384@16384 slave 2235ce74c983debc10d093d80905630f0c6a2135 0 1584287366391 6 connected
72ae914b9f1068dd2767ff3efd1b76ca149a427f 127.0.0.1:6379@16379 master - 0 1584287367398 1 connected 0-5460
5731bb3e902d9e7617c6a324b56f2dd2b53d8b80 127.0.0.1:6383@16383 slave 72ae914b9f1068dd2767ff3efd1b76ca149a427f 0 1584287367000 1 connected
2d177d9e09541ac0b0694b82fe60764d3409bbf7 127.0.0.1:6381@16381 myself,master - 0 1584287364000 3 connected 10923-16383

还可以使用 redis-cli --cluster check 127.0.0.1:6379 查看KEY、slot分布:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
[root@node2 redis]# redis-cli --cluster check 127.0.0.1:6379
127.0.0.1:6379 (72ae914b...) -> 2 keys | 5461 slots | 1 slaves.
127.0.0.1:6381 (2d177d9e...) -> 1 keys | 5461 slots | 1 slaves.
127.0.0.1:6380 (2235ce74...) -> 1 keys | 5462 slots | 1 slaves.
[OK] 4 keys in 3 masters.
0.00 keys per slot on average.
>>> Performing Cluster Check (using node 127.0.0.1:6379)
M: 72ae914b9f1068dd2767ff3efd1b76ca149a427f 127.0.0.1:6379
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: 680fbb0fb4154e4589b2d7e82a1c0b63adc68f11 127.0.0.1:6384
slots: (0 slots) slave
replicates 2235ce74c983debc10d093d80905630f0c6a2135
M: 2d177d9e09541ac0b0694b82fe60764d3409bbf7 127.0.0.1:6381
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
M: 2235ce74c983debc10d093d80905630f0c6a2135 127.0.0.1:6380
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: 5731bb3e902d9e7617c6a324b56f2dd2b53d8b80 127.0.0.1:6383
slots: (0 slots) slave
replicates 72ae914b9f1068dd2767ff3efd1b76ca149a427f
S: 7557022fc35dfc2f34bc4ed40a065e284cbbc7a4 127.0.0.1:6382
slots: (0 slots) slave
replicates 2d177d9e09541ac0b0694b82fe60764d3409bbf7
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

测试集群

随意连接一个redis,redis-cli -p 6381 -c 添加数据进行测试,可以看到KEY是分别保存到不同的redis主上面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@node2 redis]# redis-cli -p 6381 -c
127.0.0.1:6381> set k1 123
OK
127.0.0.1:6381> set k2 abc
-> Redirected to slot [449] located at 127.0.0.1:6379
OK
127.0.0.1:6379> set k3 edf
OK
127.0.0.1:6379> set k4 ggg
-> Redirected to slot [8455] located at 127.0.0.1:6380
OK
127.0.0.1:6380> KEYS *
1) "k4"
127.0.0.1:6380> get k3
-> Redirected to slot [4576] located at 127.0.0.1:6379
"edf"

有关集群节点的添加、删除等操作,请查看参考文档:

https://blog.csdn.net/men_wen/article/details/72853078

https://www.cnblogs.com/linuxk/p/10736780.html

数据库导出与导入

使用redis-dump命令来导出,此命令需要安装

1
2
3
4
5
6
7
[root@xmxyk rvm]# redis-dump -u :123456@192.168.1.55:3389 >/root/test.json
[root@xmxyk rvm]# cat /root/test.json
{"db":0,"key":"mylist","ttl":-1,"type":"list","value":["mysql","mongodb","redis"],"size":17}
{"db":0,"key":"sname","ttl":-1,"type":"set","value":["mongodb","redis"],"size":12}
{"db":0,"key":"myhash","ttl":-1,"type":"hash","value":{"f1":"helo","f2":"world"},"size":13}
{"db":0,"key":"zname","ttl":-1,"type":"zset","value":[["mongodb",0.0],["mysql",0.0]],"size":18}
{"db":0,"key":"name","ttl":-1,"type":"string","value":"fdm","size":3}

使用redis-load进行导入

1
2
3
4
5
6
7
8
9
10
11
[root@xmxyk rvm]# < /root/test.json redis-load -u :123456@192.168.1.55:3389
[root@xmxyk rvm]# /usr/local/redis/bin/redis-cli -h 192.168.1.55 -p 3389
192.168.1.55:3389> auth sghje141x
OK
192.168.1.55:3389> keys *
1) "mylist"
2) "sname"
3) "myhash"
4) "zname"
5) "name"
192.168.1.55:3389>
0%