redis
Posted xiao_pai_pai
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了redis相关的知识,希望对你有一定的参考价值。
Redis Sentinel主从从高可用解决方案
一、Sentinel介绍:
Sentinel是Redis的高可用性(HA)解决方案,由一个或多个Sentinel实例组成的Sentinel系统可以监视任意多个主服务器,以及这些主服务器属下的所有从服务器,并在被监视的主服务器进行下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器,然后由新的主服务器代替已下线的主服务器继续处理命令请求。Redis提供的sentinel(哨兵)机制,通过sentinel模式启动redis后,自动监控master/slave的运行状态,基本原理是:心跳机制+投票裁决。
Sentinel组件核心功能:
1) 监控:Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。
2) 提醒:当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。
3) 自动故障迁移:当一个主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作, 它会将失效主服务器的其中一个从服务器升级为新的主服务器, 并让失效主服务器的其他从服务器改为复制新的主服务器;
4) 通知:当客户端试图连接失效的主服务器时, 集群也会向客户端返回新主服务器的地址, 使得集群可以使用新主服务器代替失效服务器。
二、Sentinel故障转移工作图解:
1) 图一:
2) 图二:
3) 图三:
4) 图四:
三、架构部署:
1) 环境准备:
服务器用途 |
OS版本 |
软件版本 |
IP地址 |
Redis Server 01(主) |
Centos-6.8_64bit |
redis-3.2.8 |
10.83.22.85 |
Redis Server 02(从) |
Centos-6.8_64bit |
redis-3.2.8 |
10.83.22.86 |
Redis Sentinel 01 |
Centos-6.8_64bit |
redis-3.2.8 |
10.83.22.85 (与redis服务节点共用) |
Redis Sentinel 02 |
Centos-6.8_64bit |
redis-3.2.8 |
10.83.22.86 (与redis服务节点共用) |
Redis Sentinel 03 |
Centos-6.8_64bit |
redis-3.2.8 |
10.83.22.81 (只用于sentinel做选举) |
2) Redis主从配置(一主一从):
v Redis Server 01(主)节点配置:
① cd /software
② wget ftp://bqjrftp:Pass123$%^@10.83.20.27:9020/software/redis/redis-3.2.8.tar.gz
③ tar -xf redis-3.2.8.tar.gz-C /usr/local/
④ cd /usr/local/ && ln -sv redis-3.2.8/ redis
⑤ cd /usr/local/redis
⑥ make
⑦ make install
⑧ vim /etc/profile.d/redis.sh
REDIS_HOME=/usr/local/redis/src
PATH=$PATH:$REDIS_HOME
export PATH
⑨ source /etc/profile.d/redis.sh
⑩ mkdir -pv /data/redis/sentinel
11 mkdir -p /var/run/redis
12 mkdir -p /var/log/redis
13 mkdir -p /var/redis/redis_6379
14 vim /etc/redis/redis_6379.conf(编辑Redis主服务配置文件)
daemonize yes
pidfile "/var/run/redis/redis_6379.pid"
port 6379
bind 0.0.0.0
timeout 0
tcp-keepalive 0
loglevel notice
logfile "/var/log/redis/redis_6379.log"
databases 16
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename "dump_6379.rdb"
dir "/var/redis/redis_6379"
maxmemory 4gb
slave-read-only yes
slave-serve-stale-data yes
repl-disable-tcp-nodelay no
slave-priority 100
appendonly yes
appendfilename "appendonly_6379.aof"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
lua-time-limit 5000
slowlog-log-slower-than 10000
slowlog-max-len 128
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-entries 512
list-max-ziplist-value 64
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
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
# Generated by CONFIG REWRITE
15 cd /etc/init.d
16 vim redis6379(制作Redis服务器启动脚本):
#!/bin/sh
#
# Simple Redis init.d script conceived to work on Linux systems
# as it does use of the /proc filesystem.
# chkconfig: 2345 10 90
# description: redis_6379 ....
REDISPORT=6379
EXEC=/usr/local/redis/src/redis-server
CLIEXEC=/usr/local/redis/src/redis-cli
PIDFILE=/var/run/redis/redis_${REDISPORT}.pid
CONF="/etc/redis/redis_${REDISPORT}.conf"
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
;;
*)
echo "Please use start or stop or status as first argument"
;;
esac
17 chmod +x redis && chkconfig redis on
18 iptables -I INPUT 2 -p tcp -m state --state NEW -m tcp -m multiport --dports 6379 -j ACCEPT
19 service iptables save && iptables --line-numbers -L -n
20 service redis start
v Redis Server 02(从)节点配置:
① 从节点参照上面Redis Server 01(主)节点的配置步骤,唯一不同的地方是第14步的配置文件某些地方需要调整,如下:
Redis Server 02(从),配置文件调整部分:
末尾增加:slaveof 10.83.22.85 6379
② 启动Redis Server 02(从)节点服务;
service redis start
v Redis Server 主从架构部署验证:
① Redis Server 01(主)节点:
redis-cli -p 6379 info|awk -F ":" ‘/role/{print $2}‘
master
② Redis Server 02(从)节点:
redis-cli -p 6379 info|awk -F ":" ‘/role/{print $2}‘
slave
3) Redis Sentinel配置(3个监视节点):
v Redis Sentinel 01-03节点配置(完全相同):
① vim /etc/redis/sentinel.conf(编辑sentinel服务配置文件):
port 26379
dir "/data/redis/sentinel"
daemonize yes
protected-mode no
#注意这里是个坑,如果redis的配置文件redis._6379conf里面没有配置保护模式这个参数,这里的sentinel配置文件就必须配置protected-mode no关闭保护模式这个选项;如果redis_6379.con里面配置了protected-mode yes,就需要配置redis的密码;
logfile "/var/log/redis/sentinel.log"
sentinel monitor MyMaster6379 10.83.22.85 6379 2
sentinel down-after-milliseconds MyMaster6379 2000
sentinel failover-timeout MyMaster6379 6000
sentinel config-epoch MyMaster6379 1
② mkdir -pv /data/redis/sentinel
③ cd /etc/init.d
④ vim sentinel(编辑sentinel服务启动脚本)
#!/bin/sh
#
# Simple Redis_sentinel init.d script conceived to work on Linux systems
# as it does use of the /proc filesystem.
# chkconfig: 2345 10 90
# description: sentinel ....
SENTINEL_PORT=26379
EXEC=/usr/local/redis/src/redis-sentinel
PIDFILE=/var/run/redis/redis_${SENTINEL_PORT}.pid
CONF="/etc/redis/sentinel.conf"
start() {
if [ -f $PIDFILE ]
then
echo "$PIDFILE exists, process is already running or crashed"
else
echo "Starting Redis server..."
touch ${PIDFILE}
$EXEC $CONF
fi
}
stop() {
if [ ! -f $PIDFILE ]
then
echo "$PIDFILE does not exist, process is not running"
else
PID=$(cat $PIDFILE)
echo "Stopping ..."
pkill redis-sentinel
rm -rf ${PIDFILE}
while [ -f ${PIDFILE} ]
do
echo "Waiting for Redis_sentinel to shutdown ..."
sleep 1
done
echo "Redis_sentinel stopped"
fi
}
status() {
PID=$(cat $PIDFILE)
if [ ! -x /proc/${PID} ]
then
echo ‘Redis_sentinel is not running‘
else
echo "Redis_sentinel is running"
fi
}
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status
;;
restart)
stop
sleep 2
start
;;
*)
echo "echo "USAGE: $0 start|stop|restart|status""
;;
esac
⑤ chmod +x /etc/init.d/sentinel && chkconfig sentinel on
⑥ iptables -I INPUT 3 -p tcp -m state --state NEW -m tcp -m multiport --dports 26379 -j ACCEPT
⑦ service iptables save && iptables --line-numbers -L -n
⑧ service sentinel start
v Redis Sentinel系统部署验证:
[[email protected] rc5.d]# redis-cli -h 10.83.22.86 -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=MyMaster6379,status=ok,address=10.83.22.85:6379,slaves=1,sentinels=3
v Redis Sentinel系统自动主从故障转移验证:
① 手动停止Redis Server 01 的redis服务,模拟redis master故障下线:
service redis stopt
② 任意redis sentinel节点查看监控信息,发现原来redis master节点已切换到原来的一台从节点服务器上,成为新的redis master服务节点(10.83.20.25),如图:
原来的主是10.83.22.85,现在切换到了10.83.22.86
③ 连接Redis Server 节点(10.83.22.86),发现此节点已经由原来的slave升级到了master,如图:
④ 重新恢复之前模拟的故障节点服务,发现此故障节点自动加入redis 主从从架构,并成为slave节点,它的master节点也自动 切换到新的master节点上,如图:
4)利用Keepalived和脚本完成VIP的切换
两台redis主从服务器分别安装keepalived组件,并且配置VIP地址,配置监控脚本。实现的最终目标为:
1. 客户端连接redis只需要一个ip地址;
2、 如果主redis服务器的redis出现故障,vip自动迁移到从redis服务器,并且从redis服务器升级为master;
3、 如果主redis服务器的keepalived服务出现故障,vip自动迁移到从redis服务器,并且主redis服务器的redis进程自动停止,从redis服务器升级为master;
4.1 安装keepalived
使用最简单的方法yum来安装 yum -y install keepalived
4.2 配置keepalived:
4.2.1 主redis服务器keepalived配置:vim /etc/keepalived/keepalived.conf
global_defs {
lvs_id LVS_redis01
vrrp_mcast_group4 224.0.0.22
}
vrrp_script chk_redis {
script "/etc/keepalived/scripts/redis_check.sh"
weight -20
interval 12
}
vrrp_instance VI_1 {
state backup
interface eth0
virtual_router_id 52
nopreempt
priority 200
advert_int 4
virtual_ipaddress {
10.83.22.241
}
track_script {
chk_redis
}
notify_stop /etc/keepalived/scripts/keepalived_stop.sh
}
4.2.2 从redis服务器keepalived配置:
global_defs {
lvs_id LVS_redis02
vrrp_mcast_group4 224.0.0.22
}
vrrp_script chk_redis {
script "/etc/keepalived/scripts/redis_check.sh"
weight -20
interval 12
}
vrrp_instance VI_1 {
state backup
interface eth0
virtual_router_id 52
priority 100
nopreempt
advert_int 4
virtual_ipaddress {
10.83.22.241
}
track_script {
chk_redis
}
notify_stop /etc/keepalived/scripts/keepalived_stop.sh
}
4.3 keepalived的配合脚本:
mkdir /etc/keepalived/scripts/
脚本1:redis_check.sh 主要用途是用于keepalived服务运行时根据固定间隔时间执行的脚本。原理是判断本机是不是VIP地址的所有者,如果是判断redis是不是为master,如果不是master就会停止掉keepalived服务。如果本机不是VIP地址的所有者,判断本机的redis是否能够连接通,如果连接不通也停止掉keepalived服务;
vim redis_check.sh
#!/bin/bash
Vip=10.83.22.241
Port=6379
VipValue=`/sbin/ip add|grep $Vip`
RedisCli_Cmd="/usr/local/redis/src/redis-cli"
case_value(){
case $1 in
RedisRole)
Value=`$RedisCli_Cmd -p $Port info|awk -F‘[:| ]‘ ‘/role/{print $2}‘`
;;
Alive)
Value=`$RedisCli_Cmd -p $Port PING`
esac
}
sub_value(){
case_value $1
if [ $Value = "$2" ];then
exit 0
else
`which pkill` keepalived
exit 1
fi
}
if [ -n "$VipValue" ];then
while true
do
Value=`$RedisCli_Cmd -p $Port info|awk -F‘[:| ]‘ ‘/role/{print $2}‘`
if [ "$Value" == "slave" ];then
sleep 1
echo "the host redis is $Value"
else
break
fi
done
sub_value RedisRole master
else
sub_value Alive PONG
fi
脚本2:keepalived的停止脚本,配合keepalived配置文件一起来进行。如果keepalived服务被停止掉的话,就会同步执行停止redis服务的命令。保证vip漂移到另外一台redis机器上的同时redis的master角色也会漂移过去;
vim keepalived_stop.sh
#!/bin/bash
`which pkill` redis-server
脚本3:keepalived的启动脚本,用于/etc/rc.d/rc.local开机启动。在 /etc/rc.d/rc.local里面先启动redis,然后再启动keepalived.启动keepalived的时候,先检查本机的redis是不是master,如果是master就启动,如果不是master是slave,再判断vip地址是不是能够ping通,如果能够ping通再启动keepalived
vim keepalived_start.sh
#!/bin/bash
RedisRole=`/usr/local/bin/redis-cli -p 6379 info|awk -F‘[:| ]‘ ‘/role/{print $2}‘`
KeepalivedStartCmd="/etc/init.d/keepalived start"
if [ $RedisRole = "master" ];then
$KeepalivedStartCmd
else
while true;
do
sleep 1
ping 10.83.22.241 -c 1 >/dev/null 2>&1
if [ $? -eq 0 ];then $KeepalivedStartCmd;break;fi
done
fi
附录:
Redis服务配置解释:
*save <seconds> <changes>
save 900 1
save 300 10
save 60 10000
这段配置的内容表示:三个策略满足其中任意一个均会触发SNAPSHOTTING操作;900s内至少有一个key有变化
300s内至少有10个key有变化,60s内至少有1万个key发生变化;
stop-writes-on-bgsave-error yes dump操作出现错误时,是否禁止新写入操作请求;
rdbcompression yes rdbchecksum yes
dbfilename dump.rdb: 指定rdb文件名
*dir /var/lib/redis:rdb文件的存储路径
AOF相关的配置
*appendonly no
appendfilename “appendonly.aof”
*appendfsync
Redis supports three different modes;
no:redis不执行主动同步操作,而是OS进行;
everysec:每秒一次;
always:每语句一次;
no-appendfsync-on-rewrite no
是否在后台执行aof重写期间不调用fsync,默认no,表示调用;
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
上述两个条件同时满足时,会触发重写AOF;与上次aof文件大小相比,其增长量超过100%,且大小不少于64MB;
aof-load-truncated yes
注意:持久机制本身不能取代备份;应该制订备份策略;对redis库定期备份;
Redis sentinel服务配置解释:
port 26379
sentinel monitor <master-name> <ip> <redis-port>
<quorum>
sentinel auth-pass <master-name> <passowrd>
<quorum>表示sentinel集群的quorum机制,即至少有quorum个sentinel节点同时判定主节点故障时,
才认为其真的故障
s_down:subjectively down
o_down: objectively down
sentinel down-after-milliseconds <master-name><milliseconds>
监控到指定的集群的主节点异常状态持续多久才将标记为“故障”;
sentinel parallel-syncs <master-name> <numslaves>
指在failover过程中,能够被sentinel并行配置的从节点的数量;
sentinel failover-time <master-name> <milliseconds>
sentinel必须在此指定的时长内完成故障转移操作, 否则,将视为故障转移操作失败;
sentinel notification-scripts <master-name> <script-path>
通知脚本,此脚本被自动传递多个参数;
Jedis Sentinel的使用简介:
Maven依赖:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.8.0</version>
</dependency>
<!-- spring-redis -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.6.4.RELEASE</version>
</dependency>
redis的配置文件:
#redis config
redis.pass=yingjun
redis.pool.maxTotal=105
redis.pool.maxIdle=10
redis.pool.maxWaitMillis=60000
redis.pool.testOnBorrow=true
sentinel1.ip=192.168.78.99
sentinel1.port=63791
sentinel2.ip=192.168.78.99
sentinel2.port=63792
Spring的配置文件:
<!-- Redis
配置 -->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxTotal" value="${redis.pool.maxTotal}" />
<property name="maxIdle" value="${redis.pool.maxIdle}" />
<property name="maxWaitMillis" value="${redis.pool.maxWaitMillis}" />
<property name="testOnBorrow" value="${redis.pool.testOnBorrow}" />
</bean>
<bean id="sentinelConfiguration"
class="org.springframework.data.redis.connection.RedisSentinelConfiguration">
<property name="master">
<bean class="org.springframework.data.redis.connection.RedisNode">
<property name="name" value="master-1"></property>
</bean>
</property>
<property name="sentinels">
<set>
<bean class="org.springframework.data.redis.connection.RedisNode">
<constructor-arg name="host" value="${sentinel1.ip}"></constructor-arg>
<constructor-arg name="port" value="${sentinel1.port}"></constructor-arg>
</bean>
<bean class="org.springframework.data.redis.connection.RedisNode">
<constructor-arg name="host" value="${sentinel2.ip}"></constructor-arg>
<constructor-arg name="port" value="${sentinel2.port}"></constructor-arg>
</bean>
</set>
</property>
</bean>
<!-- Jedis ConnectionFactory
连接配置 -->
<bean id="jedisConnectionFactory"
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="password" value="${redis.pass}"></property>
<property name="poolConfig" >
<ref bean="jedisPoolConfig"/>
</property>
<constructor-arg name="sentinelConfig" ref="sentinelConfiguration"></constructor-arg>
</bean>
<!-- redisTemplate
配置,redisTemplate是对Jedis的对redis操作的扩展,有更多的操作,封装使操作更便捷 -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">
<property name="connectionFactory" ref="jedisConnectionFactory" />
</bean>
代码中直接用redisTemplate调用:
@Override
public boolean add(final KeyToken tkey) {
boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {
@Override
public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
RedisSerializer<String> serializer = getRedisSerializer();
byte[] key = serializer.serialize(tkey.getIndex());
byte[] name = serializer.serialize(tkey.getExpire_time());
return connection.setNX(key, name);
}
});
return result;
}
以上是关于redis的主要内容,如果未能解决你的问题,请参考以下文章