Redis3.x与4.x集群部署配置优化

参考文档

http://www.redis.cn/topics/cluster-spec.html

键分布模型

键空间被分割为 16384 槽(slot),事实上集群的最大节点数量是 16384 个。(然而建议最大节点数量设置在1000这个数量级上)
所有的主节点都负责 16384 个哈希槽中的一部分。当集群处于稳定状态时,集群中没有在执行重配置(reconfiguration)操作,每个哈希槽都只由一个节点进行处理(不过主节点可以有一个或多个从节点,可以在网络断线或节点失效时替换掉主节点)。
以下是用来把键映射到哈希槽的算法(下一段哈希标签例外就是按照这个规则):
HASH_SLOT = CRC16(key) mod 16384
其中,CRC16的定义如下:
名称:XMODEM(也可以称为 ZMODEM 或 CRC-16/ACORN)
输出长度:16 bit
多项数(poly):1021(即是 x16 + x12 + x5 + 1 )
初始化:0000
反射输入字节(Reflect Input byte):False
反射输入CRC(Reflect Output CRC):False
用于输出CRC的异或常量(Xor constant to output CRC):0000
该算法对于输入”123456789”的输出:31C3
CRC16的16位输出中的14位会被使用(这也是为什么上面的式子中有一个对 16384 取余的操作)。 在我们的测试中,CRC16能相当好地把不同的键均匀地分配到 16384 个槽中。
注意: 在本文档的附录A中有CRC16算法的实现。
Redis499问题根本原因是redis发生了阻塞,是linux系统环境(CPU/内存/IO)、Redis配置、客户端使用多方综合导致,由于大数据部门使用量巨大,需要持续优化验证。

性能优化

Redis优化相关

1、单个redis实例 内存控制在10GB以内,fork耗时每GB在20ms左右,10GB在200ms左右(latest_fork_usec:287905),需要配置maxmemory,最大内存的70%左右
2、降低fork操作频率,适度放宽rdb/aof自动触发时机(默认只开启rdb持久化且每分钟有1万key变更就触发rdb持久化)
3、repl-timeout默认60s,如果rdb(目前rdb文件压缩后有3G多,不压缩有10GB左右,默认开启压缩,目前量至少需要2-3分钟才能同步完成)60S内数据没有同步到从节点,主节点会认为从节点故障并中断复制连接
4、客户端尽量避免使用高算法复杂度的命令,可以通过info commandstats查看
可能还有其他需要关注的点,后续了解后再补充

Linux配置优化相关(只适用redis服务器)

1、关闭THP
临时
echo never > /sys/kernel/mm/transparent_hugepage/enabled
永久
/etc/rc.local
if test -f /sys/kernel/mm/redhat_transparent_hugepage/enabled; then
echo never > /sys/kernel/mm/redhat_transparent_hugepage/enabled
fi
2、内存分配策略
临时
echo 1 > /proc/sys/vm/overcommit_memory (默认0)
永久
echo "vm.overcommit_memory=1" >>/etc/sysctl.conf
3、交换分区策略
临时
echo 10 >  /proc/sys/vm/swappiness (默认60)
永久
echo "vm.swappiness=10"  >>/etc/sysctl.conf

4、分盘存储
单机多实例情况下,不同实例RDB/AOF文件分盘存储(2实例虚拟机最好挂载2块盘)
运营添加业务监控:应用方加入异常报警,发生阻塞时线上应用会最先感知到
运维部署CacheCloud监控集群状态,便于发现定位问题

补充资料

查看状态

redis-cli -c -p 7004 info persistence
redis-cli -c -p 7004 info replication
redis-cli -c -p 7004 info commandstats
redis-cli -c -p 7004 info stats
redis-cli -c -p 7004 –stat

集群客户端

使用最多的时java客户端, Jedis 最近添加了对集群的支持, 详细请查看项目README中Jedis Cluster部分.
StackExchange.Redis 提供对 C# 的支持(并且包括大部分 .NET 下面的语言,比如: VB, F#等等)
thunk-redis 提供对 Node.js 和 io.js的支持。
Redis unstable 分支中的 redis-cli 程序实现了非常基本的集群支持, 可以使用命令 redis-cli -c 来启动。
redis-cli 对集群的支持是非常基本的, 所以它总是依靠 Redis 集群节点来将它转向(redirect)至正确的节点。一个真正的(serious)集群客户端应该做得比这更好: 它应该用缓存记录起哈希槽与节点地址之间的映射(map), 从而直接将命令发送到正确的节点上面。这种映射只会在集群的配置出现某些修改时变化, 比如说, 在一次故障转移(failover)之后, 或者系统管理员通过添加节点或移除节点来修改了集群的布局(layout)之后, 诸如此类。
CONFIG SET SAVE “900 1 300 10”.
Redis 持久化
Redis 提供了不同级别的持久化方式:
RDB持久化方式能够在指定的时间间隔能对你的数据进行快照存储.
AOF持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以redis协议追加保存每次写的操作到文件末尾.Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大.
如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式.
你也可以同时开启两种持久化方式, 在这种情况下, 当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整.
最重要的事情是了解RDB和AOF持久化方式的不同,让我们以RDB持久化方式开始:
RDB的优点
RDB是一个非常紧凑的文件,它保存了某个时间点得数据集,非常适用于数据集的备份,比如你可以在每个小时报保存一下过去24小时内的数据,同时每天保存过去30天的数据,这样即使出了问题你也可以根据需求恢复到不同版本的数据集.
RDB是一个紧凑的单一文件,很方便传送到另一个远端数据中心或者亚马逊的S3(可能加密),非常适用于灾难恢复.
RDB在保存RDB文件时父进程唯一需要做的就是fork出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他IO操作,所以RDB持久化方式可以最大化redis的性能.
与AOF相比,在恢复大的数据集的时候,RDB方式会更快一些.
RDB的缺点
如果你希望在redis意外停止工作(例如电源中断)的情况下丢失的数据最少的话,那么RDB不适合你.虽然你可以配置不同的save时间点(例如每隔5分钟并且对数据集有100个写的操作),是Redis要完整的保存整个数据集是一个比较繁重的工作,你通常会每隔5分钟或者更久做一次完整的保存,万一在Redis意外宕机,你可能会丢失几分钟的数据.
RDB 需要经常fork子进程来保存数据集到硬盘上,当数据集比较大的时候,fork的过程是非常耗时的,可能会导致Redis在一些毫秒级内不能响应客户端的请求.如果数据集巨大并且CPU性能不是很好的情况下,这种情况会持续1秒,AOF也需要fork,但是你可以调节重写日志文件的频率来提高数据集的耐久度.
AOF 优点
使用AOF 会让你的Redis更加耐久: 你可以使用不同的fsync策略:无fsync,每秒fsync,每次写的时候fsync.使用默认的每秒fsync策略,Redis的性能依然很好(fsync是由后台线程进行处理的,主线程会尽力处理客户端请求),一旦出现故障,你最多丢失1秒的数据.
AOF文件是一个只进行追加的日志文件,所以不需要写入seek,即使由于某些原因(磁盘空间已满,写的过程中宕机等等)未执行完整的写入命令,你也也可使用redis-check-aof工具修复这些问题.
Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写: 重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。 整个重写操作是绝对安全的,因为 Redis 在创建新 AOF 文件的过程中,会继续将命令追加到现有的 AOF 文件里面,即使重写过程中发生停机,现有的 AOF 文件也不会丢失。 而一旦新 AOF 文件创建完毕,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操作。
AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议的格式保存, 因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析(parse)也很轻松。 导出(export) AOF 文件也非常简单: 举个例子, 如果你不小心执行了 FLUSHALL 命令, 但只要 AOF 文件未被重写, 那么只要停止服务器, 移除 AOF 文件末尾的 FLUSHALL 命令, 并重启 Redis , 就可以将数据集恢复到 FLUSHALL 执行之前的状态。
AOF 缺点
对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。
根据所使用的 fsync 策略,AOF 的速度可能会慢于 RDB 。 在一般情况下, 每秒 fsync 的性能依然非常高, 而关闭 fsync 可以让 AOF 的速度和 RDB 一样快, 即使在高负荷之下也是如此。 不过在处理巨大的写入载入时,RDB 可以提供更有保证的最大延迟时间(latency)。

部署节点

echo never > /sys/kernel/mm/redhat_transparent_hugepage/enabled
echo “vm.overcommit_memory=1” >>/etc/sysctl.conf
echo “vm.swappiness=10” >>/etc/sysctl.conf
yum -y install tcl-devel
wget http://download.redis.io/releases/redis-3.2.9.tar.gz
tar zxvf redis-3.2.9.tar.gz
cd redis-3.2.9
make
make test
make install
sh ./utils/install_server.sh
/data/redis/redis_7001.log
/data/redis/7001
Selected config:
Port : 7001
Config file : /etc/redis/7001.conf
Log file : /data/redis/redis_7001.log
Data dir : /data/redis/7001
Executable : /usr/local/bin/redis-server
Cli Executable : /usr/local/bin/redis-cli

集群管理

wget https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.4.tar.gz
tar -xzvf ruby-2.3.4.tar.gz
cd ruby-2.3.4
./configure –disable-install-rdoc
make && make install
/usr/local/bin/ruby
/usr/local/bin/gem
gem sources –add https://ruby.taobao.org/ –remove https://rubygems.org/
gem install redis
[root@redisep69 ruby-2.3.4]# gem install redis
Fetching: redis-3.3.3.gem (100%)
Successfully installed redis-3.3.3
Parsing documentation for redis-3.3.3
Installing ri documentation for redis-3.3.3
Done installing documentation for redis after 1 seconds
1 gem installed

集群节点配置

[root@redisep69 etc]# cat /etc/redis/7001.conf20170821
bind 192.168.188.69
protected-mode no
port 7001
tcp-backlog 511
timeout 0
tcp-keepalive 300
daemonize yes
supervised no
pidfile /var/run/redis_7001.pid
loglevel notice
logfile /data/redis/redis_7001.log
databases 16
save 900 100
save 300 1000
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir /data/redis/7001
slave-serve-stale-data yes
slave-read-only yes
repl-diskless-sync no
repl-diskless-sync-delay 5
repl-timeout 300
repl-disable-tcp-nodelay no
slave-priority 100
maxclients 40000
maxmemory 20gb
appendonly no
appendfilename “appendonly.aof”
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
lua-time-limit 5000
cluster-enabled yes
cluster-config-file nodes-7001.conf
cluster-node-timeout 10000
slowlog-log-slower-than 10000
slowlog-max-len 128
latency-monitor-threshold 0
notify-keyspace-events “”
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-size -2
list-compress-depth 0
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
hll-sparse-max-bytes 3000
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
aof-rewrite-incremental-fsync yes

加入集群

[root@redisep69 bin]# redis-trib.rb create --replicas 1 192.168.188.69:7001 192.168.188.70:7002 192.168.188.71:7003 192.168.188.72:7004 192.168.188.73:7005  192.168.188.74:7006
>>> Creating cluster
>>> Performing hash slots allocation on 6 nodes...
Using 3 masters:
192.168.188.69:7001
192.168.188.70:7002
192.168.188.71:7003
Adding replica 192.168.188.72:7004 to 192.168.188.69:7001
Adding replica 192.168.188.73:7005 to 192.168.188.70:7002
Adding replica 192.168.188.74:7006 to 192.168.188.71:7003
M: b44e8f7e4505180f9969ecd5fee1b15f5dc3e045 192.168.188.69:7001
slots:0-5460 (5461 slots) master
M: aec4598d4905792ec9e5d8ab7ca403d250a2e89f 192.168.188.70:7002
slots:5461-10922 (5462 slots) master
M: d3e940f440dc5b33969575924bcc416e4658cd0b 192.168.188.71:7003
slots:10923-16383 (5461 slots) master
S: 24df5294bab6cce79fb1e0a134a4f8f761f30006 192.168.188.72:7004
replicates b44e8f7e4505180f9969ecd5fee1b15f5dc3e045
S: e9fe9a686aacb76d16b28460ec17f3f79098dc22 192.168.188.73:7005
replicates aec4598d4905792ec9e5d8ab7ca403d250a2e89f
S: 57015121d70c9f89b01cca162a25397f27ba3171 192.168.188.74:7006
replicates d3e940f440dc5b33969575924bcc416e4658cd0b
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 192.168.188.69:7001)
M: b44e8f7e4505180f9969ecd5fee1b15f5dc3e045 192.168.188.69:7001
slots:0-5460 (5461 slots) master
1 additional replica(s)
M: aec4598d4905792ec9e5d8ab7ca403d250a2e89f 192.168.188.70:7002
slots:5461-10922 (5462 slots) master
1 additional replica(s)
S: 24df5294bab6cce79fb1e0a134a4f8f761f30006 192.168.188.72:7004
slots: (0 slots) slave
replicates b44e8f7e4505180f9969ecd5fee1b15f5dc3e045
S: e9fe9a686aacb76d16b28460ec17f3f79098dc22 192.168.188.73:7005
slots: (0 slots) slave
replicates aec4598d4905792ec9e5d8ab7ca403d250a2e89f
S: 57015121d70c9f89b01cca162a25397f27ba3171 192.168.188.74:7006
slots: (0 slots) slave
replicates d3e940f440dc5b33969575924bcc416e4658cd0b
M: d3e940f440dc5b33969575924bcc416e4658cd0b 192.168.188.71:7003
slots:10923-16383 (5461 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
保存节点配置到硬盘(默认会保存)
[root@redisep69 bin]# redis-cli -c -h 192.168.188.69 -p 7001
192.168.188.69:7001> CLUSTER SAVECONFIG
OK
192.168.188.69:7001>

压力测试

[root@redisep69 etc]# redis-benchmark -d 100  -n 2000000 -t get,set  -q  -h 192.168.188.69 -p 7001
SET: 120685.49 requests per second
GET: 152753.38 requests per second
[root@redisep69 etc]# redis-benchmark -d 10  -n 2000000 -t get,set  -q  -h 192.168.188.69 -p 7001
SET: 148754.19 requests per second
GET: 126887.45 requests per second
[root@redisep69 etc]# redis-benchmark -d 1  -n 2000000 -t get,set  -q  -h 192.168.188.69 -p 7001
SET: 141522.78 requests per second
GET: 158516.30 requests per second
[root@redisep71 ~]# redis-benchmark -d 100  -n 2000000 -t get,set  -q  -h 192.168.188.71 -p 7003
SET: 176928.52 requests per second

三节点集群注意事项

REDIS3节点集群默认会有一组主从在同一虚拟机上,需要手工调整,架构主-从为7001-7006  7002-7004  7003-7005,以下是调整主从例子。
[root@redis-108 ~]# redis-cli -c -h 172.17.6.110 -p 7006
172.17.6.110:7006> CLUSTER REPLICATE 560eca92df8080db9e0b3d8cf65ac1ed00c30dd0
OK
172.17.6.110:7006> exit
[root@redis-108 ~]# redis-cli -c -h 172.17.6.109 -p 7005
172.17.6.109:7005> CLUSTER REPLICATE 33d3ee604bc6136283038db12967c0dfb09e48a1
OK
172.17.6.109:7005> CLUSTER SAVECONFIG
OK

添加zabbix监控方法

zabbix添加redis模版方法
第一步
vim /usr/local/zabbix/etc/zabbix_agentd.conf
UserParameter=redis.discovery,/usr/local/zabbix/etc/redis_port.sh
UserParameter=redis_stats[*],redis-cli -h 172.17.6.108 -a redis_passwd -p $1 info|grep $2|cut -d : -f2

第二步
visudo
Defaults:zabbix  !requiretty
zabbix ALL=(root)  NOPASSWD:/bin/netstat
具体sh脚本如下
cat /usr/local/zabbix/etc/redis_port.sh

#!/bin/bash
#Fucation:mysql low-level discovery
#Script_name redis_low_discovery.sh
redis() {
port=($(sudo netstat -tpln | awk -F "[ :]+" '/redis/ && /0.0.0.0/ {print $5}'))
printf '{\n'
printf '\t"data":[\n'
for key in ${!port[@]}
do
if [[ "${#port[@]}" -gt 1 && "${key}" -ne "$((${#port[@]}-1))" ]];then
socket=`ps aux|grep ${port[${key}]}|grep -v grep|awk -F '=' '{print $10}'|cut -d ' ' -f 1`
printf '\t {\n'
printf "\t\t\t\"{#REDISPORT}\":\"${port[${key}]}\"},\n"
else [[ "${key}" -eq "((${#port[@]}-1))" ]]
socket=`ps aux|grep ${port[${key}]}|grep -v grep|awk -F '=' '{print $10}'|cut -d ' ' -f 1`
printf '\t {\n'
printf "\t\t\t\"{#REDISPORT}\":\"${port[${key}]}\"\n\t }\n"
fi
done
printf '\t ]\n'
printf '}\n'
}
redis  $1