Redis 3.2 安装及主从复制详细配置

Posted by Sunday on 2018-09-28

编译安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
cd /usr/local/src
wget http://download.redis.io/releases/redis-3.2.12.tar.gz
tar xf redis-3.2.10.tar.gz
cd redis-3.2.10
make PREFIX=/usr/local/redis install

useradd -s /bin/false -M redis
mkdir /data/redis
chown redis.redis /data/redis
chmod 755 /data/redis

cp redis.conf /etc/redis/6379.conf
cp utils/redis_init_script /etc/init.d/redis
sed -i 's#^PIDFILE.*#PIDFILE=/var/run/redis/redis_${REDISPORT}.pid#g' /etc/init.d/redis

mkdir -pv /var/{run,log}/redis
chown redis.redis /var/{run,log}/redis

配置文件

1
2
3
4
5
6
7
daemonize yes # 后台运行
bind 127.0.0.1 192.168.1.41 # 绑定多IP
requirepass MbGlEnjqZ2Op+2HeLETjm@IT # 设置登陆密码
logfile "/var/log/redis/redis.log" # 日志
dir /data/redis # 数据目录
appendonly yes # 开启aof 持久存储
masterauth MbGlEnjqZ2Op+2HeLETjm@IT # 主从密码

快速脚本

1
2
3
4
5
6
7
sed -i 's#^daemonize.*#daemonize yes#' /etc/redis/6379.conf
sed -i 's#^logfile.*#logfile "/var/log/redis/redis.log"#' /etc/redis/6379.conf
sed -i 's#^pidfile.*#pidfile /var/run/redis/redis_6379.pid#' /etc/redis/6379.conf
sed -i 's#^dir ./#dir /data/redis#' /etc/redis/6379.conf
sed -i 's#^appendonly.*#appendonly yes#' /etc/redis/6379.conf
sed -i 's!# requirepass foobared!# requirepass foobared\nrequirepass MbGlEnjqZ2Op+2HeLETjm@IT' /etc/redis/6379.conf
sed -i 's!# masterauth.*#masterauth MbGlEnjqZ2Op+2HeLETjm@IT' /etc/redis/6379.conf

启动脚本

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
#!/bin/sh

### BEGIN INIT INFO
# Provides: redis
# Required-Start: $local_fs $network
# Required-Stop: $local_fs $network
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: redis-server - Persistent key-value db
# Description: redis-server - Persistent key-value db
### END INIT INFO

# chkconfig: 2345 90 10

REDISPORT=6379
EXEC=/usr/local/redis/bin/redis-server
CLIEXEC=/usr/local/redis/bin/redis-cli

PIDFILE=/var/run/redis/redis_${REDISPORT}.pid
CONF="/etc/redis/${REDISPORT}.conf"
SUDOUSER="sudo -u redis"
PASSWORD="-a MbGlEnjqZ2Op+2HeLETjm@IT"

if test -f /sys/kernel/mm/transparent_hugepage/enabled; then
echo never > /sys/kernel/mm/transparent_hugepage/enabled
fi

case "$1" in
start)
if [ -f $PIDFILE ]
then
echo "$PIDFILE exists, process is already running or crashed"
else
echo "Starting Redis server..."
#$EXEC $CONF
$SUDOUSER $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
$CLIEXEC -p $REDISPORT $PASSWORD shutdown
while [ -x /proc/${PID} ]
do
echo "Waiting for Redis to shutdown ..."
sleep 1
done
echo "Redis stopped"
fi
;;
restart)
$0 stop;
$0 start;
;;
*)
echo "Please use start or stop as first argument"
;;
esac
1
2
3
4
chkconfig --add redis # CentOS
chkconfig redis on
# update-rc.d redis enable # Ubuntu
# update-rc.d redis defaults

修改内核参数

1
2
3
4
5
tail /var/log/redis/redis.log 
8989:S 17 Aug 15:33:05.472 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
8989:S 17 Aug 15:33:05.472 # Server started, Redis version 3.2.3
8989:S 17 Aug 15:33:05.472 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
8989:S 17 Aug 15:33:05.472 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.
1
2
3
4
5
6
cat << EOF >> /etc/sysctl.conf
net.core.somaxconn = 262144 #最大队列长度,应付突发的大并发连接请求,默认为128
net.core.netdev_max_backlog = 262144 #半连接队列长度,此值受限于内存大小,默认为1024
vm.overcommit_memory = 1
EOF
sysctl -p #参数生效

vm.overcommit_memory不同的值说明

redis的数据回写机制分为两种

同步回写即SAVE命令。redis主进程直接写数据到磁盘。当数据量大时,这个命令将阻塞,响应时间长
异步回写即BGSAVE命令。redis 主进程fork一个子进程,复制主进程的内存并通过子进程回写数据到磁盘。
由于RDB文件写的时候fork一个子进程。相当于复制了一个内存镜像。当时系统的内存是4G,而redis占用了
近3G的内存,因此肯定会报内存无法分配。如果 「vm.overcommit_memory」设置为0,在可用内存不足的情况
下,就无法分配新的内存。如果 「vm.overcommit_memory」设置为1。 那么redis将使用交换内存。

关闭透明内存
Transparent Huge Pages (THP)告警,这是一个关于透明内存巨页的话题。简单来说内存可管理的最小
单位是page,一个page通常是4kb,那1M内存就会有256个page,CPU通过内置的内存管理单元管理page表
记录。Huge Pages就是表示page的大小已超过4kb了,一般是2M到1G,它的出现主要是为了管理超大内存。
个人理解上TB的内存。而THP就是管理Huge Pages的一个抽象层次,根据一些资料显示THP会导致内存锁
影响性能,所以一般建议关闭此功能。

1
2
3
4
5
6
7
cat /sys/kernel/mm/transparent_hugepage/enabled
always [madvise] never
# always 尽量使用透明内存,扫描内存,有512个 4k页面可以整合,就整合成一个2M的页面
# never 关闭,不使用透明内存
# madvise 避免改变内存占用

echo never > /sys/kernel/mm/transparent_hugepage/enabled >> /etc/rc.local #在开机脚本里追加此命令

持久化

RDB模式
RDB是redis对数据进行持久化而保存到硬盘的数据文件。(默认)

工作原理
当redis生成dump.rdb文件时,工作过程如下:
redis主进程fork一个子进程
fork出来的子进程将内存的数据集dump到临时的RDB中
当子进程对临时的RDB文件写入完毕,redis用新的RDB文件代替旧的RDB文件

默认情况下相关配置如下:

1
2
3
save 900 1
save 300 10
save 60 10000

其意义为:

1
2
3
1key更新值时每900秒保存一次数据到硬盘
10key更新值时每300秒保存一次到硬盘
10000key更新值时每60秒保存一次到硬盘

换句话说,当你重启服务器时数据是可能会丢失的,如果数据量小的时候,你会丢失5分钟以内的数据;如果数据量大的时候,你会丢失一分钟以内的数据。

所以set name 1后重启服务器,get name时的结果会是nil,如果你想避免这种情况发生,那么可以save后重启服务器。

然而大多数情况下我们需要防止的是服务器突发情况下的重启,这时候可能没有机会save,所以还是会造成数据的丢失。所以你可以设置save “”这样的话会每次写操作到保存到硬盘,但是redis作为缓存却需要每次到保存到硬盘已丧失了其作为缓存的意义,因此这种方法是不可取的。

还有一种方式,在每次写操作时使用bgsave命令,可以直接返回操作结果并异步将其保存到硬盘。

rdb方式的优点是保存的数据存储量小(只有数据)重启速度很快;缺点是可能会造成数据的丢失。

AOF模式
aof本质是redis操作(写操作)日志文件。aof默认是未开启的,需要在配置文件中进行设置,在配置文件中将这一行改为appendonly yes就可以了。

工作原理

1
2
3
AOF :append only file
每当Redis执行一个改变数据集的命令时,这个命令都会被追加到AOF文件的末尾。
当redis重新启动时,程序可以通过AOF文件恢复数据。

那么会在什么时候append到文件末尾呢?有三种方式:

1
2
3
4
5
6
7
8
9
#appendfsync always
appendfsync everysec
#appendfsync no

appendfsync always 每次有新命令追加到 AOF 文件时就执行一次 fsync :非常慢,也非常安全
appendfsync everysec 每秒 fsync 一次:足够快(和使用 RDB 持久化差不多),并且在故障时只会丢失 1 秒钟的数据。
appendfsync no # 从不 fsync 将数据交给操作系统来处理。更快,也更不安全的选择。

推荐默认appendfsync everysec 每秒 fsync 一次, 这种 fsync 策略可以兼顾速度和安全性。

aof能够保证数据的安全,但是在重启时比较耗时,而且aof文件的体积比rdb文件大。

使用
在同时开启rdb和aof模式时,会采用aof模式来读取数据。在正常的使用中,如果不是十分在乎短时间内的数据丢失的时候,使用rdb方式会使服务器的效率更高,更节省cpu和硬盘;如果担心数据丢失的话,aof方式无疑会是更好的选择。

在线动态从RDB切换为AOF

切勿修改配置文件重启,这样会丢失所有数据的。
正确的操作方式如下

1
2
3
4
5
6
config set appendonly yes
config set save "" #关闭,不关闭会增加io压力

vim /etc/redis/6379.conf
save "" # 关闭RDB
appendonly yes # 开启AOF

主从复制

1、概述

Redis的replication机制允许slave从master那里通过网络传输拷贝到完整的数据备份。具有以下特点:

  • 1.异步复制,从2.8版本开始,slave能不时地从master那里获取到数据。*

  • 2.允许单个master配置多个slave*

  • 3.slave允许其它slave连接到自己。一个slave除了可以连接master外,它还可以连接其它的slave。形成一个图状的架构。*

  • 4.master在进行replication时是非阻塞的,这意味着在replication期间,master依然能够处理客户端的请求。*

  • 5.slave在replication期间也是非阻塞的,也可以接受来自客户端的请求,但是它用的是之前的旧数据。可以通过配置来决定slave是否在进行replication时用旧数据响应客户端的请求,如果配置为否,那么slave将会返回一个错误消息给客户端。不过当新的数据接收完全后,必须将新数据与旧数据替换,即删除旧数据,在替换数据的这个时间窗口内,slave将会拒绝客户端的请求和连接。*

  • 6.一般使用replication来可以实现扩展性,例如说,可以将多个slave配置为“只读”,或者是纯粹的数据冗余备份。*

  • 7.能够通过replication来避免master每次持久化时都将整个数据集持久化到硬盘中。只需把master配置为不进行持久化操作(把配置文件中持久化相关的配置项注释掉即可),然后连接上一个slave,这个slave则被配置持久化选项。不过需要注意的是,在这个方案中,必须确保master不会自动启动。

2、Master持久化功能关闭时Replication的安全性

当有需要使用到replication机制时,一般都会强烈建议把master的持久化开关打开。即使为了避免持久化带来的延迟影响,不把持久化开关打开,那么也应该把master配置为不会自动启动的。

为了更好地理解当一个不进行持久化的master如果允许自动启动所带来的危险性。可以看看下面这种失败情形:

假设我们有一个redis节点A,设置为master,并且关闭持久化功能,另外两个节点B和C是它的slave,并从A复制数据。
如果A节点崩溃了导致所有的数据都丢失了,它会有重启系统来重启进程。但是由于持久化功能被关闭了,所以即使它重启了,它的数据集是空的。
而B和C依然会通过replication机制从A复制数据,所以B和C会从A那里复制到一份空的数据集,并用这份空的数据集将自己本身的非空的数据集替换掉。于是就相当于丢失了所有的数据。

即使使用一些HA工具,比如说sentinel来监控master-slaves集群,也会发生上述的情形,因为master可能崩溃后迅速恢复。速度太快而导致sentinel无法察觉到一个failure的发生。

当数据的安全很重要、持久化开关被关闭并且有replication发生的时候,那么应该禁止实例的自启动

3、replication工作原理
如果你为master配置了一个slave,不管这个slave是否是第一次连接上Master,它都会发送一个SYNC命令给master请求复制数据。

master收到SYNC命令后,会在后台进行数据持久化,持久化期间,master会继续接收客户端的请求,它会把这些可能修改数据集的请求缓存在内存中。当持久化进行完毕以后,master会把这份数据集发送给slave,slave会把接收到的数据进行持久化,然后再加载到内存中。然后,master再将之前缓存在内存中的命令发送给slave。

当master与slave之间的连接由于某些原因而断开时,slave能够自动重连master,如果master收到了多个slave并发连接请求,它只会进行一次持久化,而不是一个连接一次,然后再把这一份持久化的数据发送给多个并发连接的slave。

当master和slave断开重连后,一般都会对整份数据进行复制。但从redis2.8版本开始,支持部分复制。

4、数据部分复制
从2.8版本开始,slave与master能够在网络连接断开重连后只进行部分数据复制。

master会在其内存中创建一个复制流的等待队列,master和它所有的slave都维护了复制的数据下标和master的进程id,因此,当网络连接断开后,slave会请求master继续进行未完成的复制,从所记录的数据下标开始。如果进程id变化了,或者数据下标不可用,那么将会进行一次全部数据的复制。

5、支持部分数据复制的命令是PSYNC

不需硬盘参与的Replication
一般情况下,一次复制需要将内存的数据写到硬盘中,再将数据从硬盘读进内存,再发送给slave。

对于速度比较慢的硬盘,这个操作会给master带来性能上的损失。Redis2.8版本开始,实验性地加上了无硬盘复制的功能。这个功能能将数据从内存中直接发送到slave,而不用经过硬盘的存储。

不过这个功能目前处于实验阶段,还未正式发布。

6、主从配置
slave节点配置文件

1
2
slaveof 192.168.1.41 6379
masterauth MbGlEnjqZ2Op+2HeLETjm@IT #若master通过requirepass配置了密码,则需要配置此项

7、只读的slave
从redis2.6版本开始,slave支持只读模式,而且是默认的。可以通过配置项slave-read-only来进行配置,并且支持客户端使用CONFIG SET命令来动态修改配置。

只读的slave会拒绝所有的写请求,只读的slave并不是为了防范不可信的客户端,毕竟一些管理命令例如DEBUG和CONFIG在只读模式下还是可以使用的。如果确实要确保安全性,那么可以在配置文件中将一些命令重新命名。

也许你会感到很奇怪,为什么能够将一个只读模式的slave恢复为可写的呢,尽管可写,但是只要slave一同步master的数据,就会丢失那些写在slave的数据。不过还是有一些合法的应用场景需要存储瞬时数据会用到这个特性。

1
slave-read-only yes

8、至少N个slave才允许向master写数据
从redis2.8版本开始,master可以被配置为,只有当master当前有至少N个slave连接着的时候才接受写数据的请求。

然而,由于redis是异步复制的,所以它并不能保证slave会受到一个写请求,所以总有一个数据丢失的时间窗口存在。

这个机制的工作原理如下所示:

  • slave每秒发送ping心跳给master,询问当前复制了多少数据。
  • master会记录下它上次收到某个slave的ping心跳是什么时候。
  • 使用者可以配置一个时间,来指定ping心跳的发送不应超过的一个超时时间
  • 如果master有至少N个slave,并且ping心跳的超时不超过M秒,那么它就会接收写请求。

也许你会认为这情形好似CAP理论中弱化版的C(consistency),因为写请求并不能保证数据的一致性,但这样做,至少数据丢失被限制在了限定的时间内。即M秒。

如果N和M的条件都无法达到,那么master会回复一个错误信息。写请求也不会被处理。

有两个配置项用来配置上文中提到的N和M:

1
2
min-slaves-to-write <number of slaves>
min-slaves-max-lag <number of seconds>

常用命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

127.0.0.1:6379> auth Passwd # 登录,密码验证 #redis-cli -a Passwd
OK
127.0.0.1:6379>info # 查看数据库状态
127.0.0.1:6379>info replication # 查看slave的复制状态
127.0.0.1:6379>set key 123 # 插入数据
127.0.0.1:6379>keys * # 列出数据
127.0.0.1:6379>flushdb # 清空当前数据
127.0.0.1:6379>flushall # 清除所有数据库

# 判断主从是否完全一致
dbsize #查看key 的数目
debug digest #对整个数据库的数据,产生一个摘要,可用于验证两个redis数据库数据是否一致
127.0.0.1:6379> debug digest 7164ae8b6730c8bcade46532e5e4a8015d4cccfb
127.0.0.1:6379> debug digest 7164ae8b6730c8bcade46532e5e4a8015d4cccfb

压力测试

1
2
3
4
# 测试SET 随机数性能
redis-benchmark -h 192.168.10.21 -t set -n 100000 -r 10000000
# 并发50,测试GET,SET
redis-benchmark -h 192.168.10.21 -t set,get -n 100000 -c 50 -r 10000000 -q

https://segmentfault.com/a/1190000006619753#articleHeader0
https://www.imooc.com/article/69685?block_id=tuijian_wz
https://www.jianshu.com/u/baad5ee99ca9
redis压力测试