Redis HA架构:Redis(master/slave) + Keepalived + Redis Sentinel + Twemproxy + Twemproxy Agent

一、Redis HA架构描述:
目前,Redis高可用方案,主要集中于以下几种:
1.Redis(master/slave)
2.Redis(master/slave) + Keepalived + Redis Sentinel
3.Codis
4.Redis(master/slave) + Keepalived + Redis Sentinel + Twemproxy + Keepalived
5.Redis(master/slave) + Keepalived + Redis Sentinel + Twemproxy + Twemproxy Agent + nodejs +npm
第 4 种,我们通过 两个keepalived 来是实现HA,一个是实现Redis HA的vip ;一个是Twemproxy HA的VIP
第 5 种,使用一个 keepalived 来实现Twemproxy HA,通过 Twemproxy Agent 来更新 Twemproxy 配置(当redis主从切换时)
下面,我们主要讲解第 5 种 方案:
Redis(master/slave) + Keepalived + Redis Sentinel + Twemproxy + Twemproxy Agent + nodejs + nodejs-forever-agent + npm

二、Redis HA架构图:

Image

三、Redis(master/slave) + Redis Sentinel 部署:
具体的安装可以参照:
Redis HA 下的
20141202:Redis 主从部署
20150421:Redis Sentinel + keepalived 部署
下面,将简单描述:
1.Redis(master/slave) 的部署:[ 部署在 10.55、10.56 ]
10.55、10.56 都部署两套Redis,
# 第二套redis,只要将配置文件( redis.conf) 复制并改名为 redis_6380.conf
# 新配置文件/ 启动脚本等 修改,参照 Redis 主从部署一篇
2.Redis Sentinel 部署:[ 部署在 10.54、10.55、10.56 ]
# 只要在Redis Sentinel 的配置文件,把新建的Redis master 添加进去,就可;
# 具体,参照如下文件:
vim /usr/local/redis-sentinel/etc/sentinel.conf
port 26379
dir /usr/local/redis-sentinel/data
sentinel monitor mymaster 172.16.10.55 6379 1
sentinel down-after-milliseconds mymaster 5000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 60000
# sentinel client-reconfig-script mymaster /usr/local/redis-sentinel/bin/keepalived.sh # 这里我们在故障切换时,就不再执行脚本
sentinel monitor mymaster2 172.16.10.56 6380 1
sentinel down-after-milliseconds mymaster2 5000
sentinel parallel-syncs mymaster2 1
sentinel failover-timeout mymaster2 60000
3.下面启动相关Redis服务:
(1).启动 Redis服务;
(2).启动 Redis Sentinel 服务;
(3).测试 Redis master/slave 切换情况;

四、keepalived 部署:[ 部署在 10.53、10.54 ]
# keepalived 部署 参照 ”20150421:Redis Sentinel + keepalived 部署 “ 文章中的 keepalived 部署 一段
# 在此,就不再赘述

五、Twemproxy 部署:[ 部署在 10.53、10.54 ]
# Twemproxy 部署 参照 “Redis 中间件” 中的 “20150427:twemproxy 简介、安装配置”文章
# 在此,就不再赘述

六、Twemproxy Agent 部署:[ 部署在 10.53、10.54 ]
1.安装nodejs + npm:
yum -y install nodejs npm
2.安装forever:
npm install forever -g
3.安装Twemproxy Agent
(1).解压Twemproxy Agent 包,并复制到指定目录:
mv /tmp/redis-twemproxy-agent-master /usr/local/redis-twemproxy-agent
(2).修改redis-twemproxy-agent/lib/cli.js文件:
修改cli.parse里的文件路径为部署的路径,具体参照如下配置:
cli.parse
({
host: [‘h’, ‘Redis sentinel hostname’, ‘string’, ‘172.16.10.54’],
port: [‘p’, ‘Redis sentinel port number’, ‘number’, 26379],
config: [‘f’, ‘Path to twemproxy config’, ‘path’, ‘/usr/local/twemproxy/conf/nutcracker.yml’],
command: [‘c’, ‘Command to restart twemproxy’, ‘string’, ‘/etc/init.d/twemproxy restart’],
log: [‘l’, ‘The log file location’, ‘string’, ‘/usr/local/twemproxy/run/twemproxy_sentinel.log’]
});
(3).将 redis-twemproxy-agent/init.d/twemproxy_sentinel 复制到 /etc/init.d/ 目录下:
cp /usr/local/redis-twemproxy-agent/init.d/twemproxy_sentinel /etc/init.d/

# 修改该脚本redis-twemproxy-agent,nodejs,forever,的路径为实际路径,参考如下:
ROOT=/usr/local/redis-twemproxy-agent
EXEC=$ROOT/bin/redis_twemproxy_agent
NODE=/usr/bin/node
FOREVER=/usr/bin/forever
(4).启动 redis-twemproxy-agent 服务:
/etc/init.d/twemproxy_sentinel start

# 此测试结果,没有能证明 redis-twemproxy-agent 能自动修改 twemproxy 的配置文件,可能在文件配置方面有误,后续再研究

Redis Sentinel + keepalived 部署

一、Redis Sentinel简介:
Redis Sentinel 是一个分布式系统,你可以在一个架构中运行多个Sentinel 进程(progress),这些进程使用流言协议(gossipprotocols) 来接收关于主服务器是否下线的信息,并使用投票协议(agreement protocols)来决定是否执行自动故障迁移,以及选择哪个从服务器作为新的主服务器。
虽然Redis Sentinel只是一个单独的可执行文件 redis-sentinel,但实际上它只是一个运行在特殊模式下的 Redis 服务器,你可以在启动一个普通 Redis 服务器时通过给定 –sentinel 选项来启动 Redis Sentinel。
源码编译安装后,src目录有一个命名为redis-sentinel的程序。
对于redis-sentinel 程序,你可以用以下命令来启动Sentinel 系统:redis-sentinel /path/to/sentinel.conf

二、Redis Sentinel 的部署:
1.复制 redis 目录为 Redis Sentinel
# 这里我们基于原先 源码编译安装的redis目录,复制一个副本,改名为: Redis Sentinel
cp -r /usr/local/redis-3.0 /usr/local/redis-sentinel

2.正常安装:我们需要建 etc、bin、data、log 等目录;
但是,因为我们是复制redis目录,这写目录都有了,我们只需要稍作改动:
# 在 10.54、10.55 、 10.56 的redis上,都配置此文件,只是在 10.54[ 此机器上没有装redis ]上的配置文件 sentinel client-reconfig-script 注释掉
vim /usr/local/redis-sentinel/etc/sentinel.conf
port 26379
dir /usr/local/redis-sentinel/data
sentinel monitor mymaster 172.16.10.55 6379 1
sentinel down-after-milliseconds mymaster 5000 # 单位为毫秒
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 60000
sentinel client-reconfig-script mymaster /usr/local/redis-sentinel/bin/keepalived.sh # 检测故障后,调用的keepalive脚本,需要有X权限

3.部署redis-sentinel 启动脚本:
vim /usr/local/redis-sentinel/bin/startup.sh
#!/bin/bash
CMD_PATH=/usr/local/redis-sentinel/bin/redis-sentinel
CONF_PATH=/usr/local/redis-sentinel/conf/sentinel.conf
LOG_PATH=/usr/local/redis-sentinel/log/sentinel.log
${CMD_PATH} ${CONF_PATH} >> ${LOG_PATH} 2>&1 &

以下为参考脚本,暂不使用:
#!/bin/bash
#Configurations injected by install_server below….
EXEC=/usr/local/redis-sentinel/bin/redis-sentinel
CLIEXEC=/usr/local/redis-sentinel/bin/redis-cli
PIDFILE=/usr/local/redis-sentinel/log/redis-sentinel_26379.pid
CONF=”/usr/local/redis-sentinel/etc/sentinel.conf”
REDISPORT=”26379″
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

三、keepalived 部署:
1.keepalive的安装,安装目录为/usr/local/keepalive
# 默认软件已上传并且解压,进入keepalived目录
./configure –prefix=/usr/local/keepalived
make && make install

# 在配置过程中可能出现某些软件包没有正确安装的提示,如openssl,popt等。
解决办法是使用 yum install openssl*以及yum install popt popt-deve l来安装缺少的软件包。
Keepalived 安装完成后,会在安装目录/usr/local/keepalived 生成 bin,etc,man,sbin 这4 个目录。其中etc为配置文件所在的目录。

2.配置 keepalived 环境变量
# 给root用户增加环境变量,在export PATH行之前加入一行:
vi ~/.bash_profile
PATH=$PATH:/usr/local/keepalived/sbin
保存退出,执行下面命令,使参数生效
. ~/.bash_profile

3.整理相关文件和命令,方便使用service 启动
cp -a /usr/local/keepalived/sbin/keepalived /usr/sbin/
cp -a /usr/local/keepalived/etc/sysconfig/keepalived /etc/sysconfig/
cp -a /usr/local/keepalived/etc/rc.d/init.d/keepalived /etc/init.d/
# 让chkconfig管理新添加的服务
chkconfig –add keepalived
# 添加启动级别
chkconfig keepalived on
# 查看启动设置
chkconfig –list keepalived

4.keepalived配置文件的修改如下:配置文件目录在( /etc/keepalived/keepalived.conf )
# 172.16.10.55 master 的配置文件
global_defs {
notification_email
{
13501960667@139.com # 收件邮箱
}
notification_email_from mysql_lab@localhost # 发件邮箱
smtp_server localhost # 发件服务器
smtp_connect_timeout 30 # 超时时间
router_id HAOWU_55
}
vrrp_sync_group VG1 {
group
{
VI_1
}
}
vrrp_instance VI_1 {
state MASTER
# state BACKUP
# nopreempt
interface eth0
virtual_router_id 96
priority 110
advert_int 1
authentication
{
auth_type PASS
auth_pass Pa$$w0rd
}
virtual_ipaddress
{
172.16.10.251
}
}

# 172.16.10.56 slave 的配置文件
global_defs {
notification_email
{
13501960667@139.com
}
notification_email_from mysql_lab@localhost
smtp_server localhost
smtp_connect_timeout 30
router_id HAOWU_56
}
vrrp_sync_group VG1 {
group
{
VI_1
}
}
vrrp_instance VI_1 {
state BACKUP
interface eth0
virtual_router_id 96
priority 100
advert_int 1
authentication
{
auth_type PASS
auth_pass Pa$$w0rd
}
virtual_ipaddress
{
172.16.10.251
}
}

注:
# 红色部分 是在我们实际配置中,需要修改的地方;
# 紫红色部分 是我们在HA操作中,可以参考的设置下:
具体理解如下:
主/从的state 都设置为: BACKUP ,同是将 priority( 110>100 ) 大的机器设置为 nopreempt 可以避免切换后,vip抢占问题;
nopreempt 只能工作于 state 为BACKUP 的状态下;

5.编写keepalived 切换脚本(55、56上配置此脚本):
vim /usr/local/redis-sentinel/bin/keepalived.sh
# 红色加粗脚本,只适用于主机宕机
#!/bin/bash
REDISCLI=”/usr/local/redis-3.0/bin/redis-cli”
if $REDISCLI info|grep role|grep -q master ;then
echo “start keepalived” >> ../log/sentinel.log
/etc/init.d/keepalived start >> ../log/sentinel.log
else
echo “stop keepalived” >> ../log/sentinel.log
/etc/init.d/keepalived stop >> ../log/sentinel.log
fi

# 下面脚本为改进脚本:
#!/bin/bash
EXEC=/usr/local/redis-3.0/bin/redis-server
CLIEXEC=/usr/local/redis-3.0/bin/redis-cli
CONF=”/usr/local/redis-3.0/etc/redis.conf”
REDISPORT=”6379″
KEEP_PID=`ps -A | grep keepalived |awk ‘{print $1}’ > /dev/null`

$CLIEXEC -p $REDISPORT info > /dev/null
if [ $? -eq 0 ]; then
if [ -z “$KEEP_PID” ]; then
# try to start keepalived again
/etc/init.d/keepalived start
else
echo “ERROR!!! server:keepalved is down!” | mail -s “ERROR!! keepalived is down !!!” 13501960667@139.com
fi
else
# try to start it again
# 以下部分 为了测试需要需要注释掉[ 下面这段脚本是判断能否重启redis,这里为测试需要,注释掉 ]
# $EXEC $CONF
# $CLIEXEC -p $REDISPORT info > /dev/null
# if [ $? -eq 0 ]; then
# exit 0
# else
# restart failed
/etc/init.d/keepalived stop
# fi
fi

# 给予脚本 执行权限:
chmod +x /usr/local/redis-sentinel/bin/keepalived.sh

四、部署测试:
1.测试keepalived 虚 ip 切换情况:
master(55) 和 slave(56) :
# 启动 keepalived
/etc/init.d/keepalived start
# 执行 ip addr:查看当前虚ip情况(停掉matser 的keepalived 看 虚IP 是否飘到 slave 上)

2.redis-sentinel启动测试:
/usr/local/redis-sentinel/bin/redis-sentinel /usr/local/redis-sentinel/etc/sentinel.conf
# 启动后,会有详细的信息输出,包括当前的主,redis-sentinel的个数,地址等

# 登陆 redis-sentinel 查看相关信息:
bin/redis-cli -h 172.16.10.56 -p 26379
172.16.10.56:26379> info sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
master0:name=mymaster,status=ok,address=172.16.10.55:6379,slaves=1,sentinels=3

3.redis 故障测试:
# 将 当前master(172.16.10.55) 关闭,看redis-sentinel 的切换日志
bin/redis-cli -h 172.16.10.55 -p 6379 shutdown
# 切换日志如下:
15296:X 22 Apr 16:38:49.292 # +sdown master mymaster 172.16.10.55 6379
15296:X 22 Apr 16:38:49.292 # +odown master mymaster 172.16.10.55 6379 #quorum 1/1
15296:X 22 Apr 16:38:49.292 # Next failover delay: I will not start a failover before Wed Apr 22 16:40:49 2015
15296:X 22 Apr 16:38:50.313 # +config-update-from sentinel 172.16.10.54:26379 172.16.10.54 26379 @ mymaster 172.16.10.55 6379
15296:X 22 Apr 16:38:50.313 # +switch-master mymaster 172.16.10.55 6379 172.16.10.56 6379
15296:X 22 Apr 16:38:50.313 * +slave slave 172.16.10.55:6379 172.16.10.55 6379 @ mymaster 172.16.10.56 6379
15296:X 22 Apr 16:38:50.441 # -script-error /usr/local/redis-sentinel/bin/keepalived.sh 0 2
15296:X 22 Apr 16:38:55.342 # +sdown slave 172.16.10.55:6379 172.16.10.55 6379 @ mymaster 172.16.10.56 6379

Redis 主从部署

Redis 部署说明:
redis使用两台机器,172.16.10.55,172.16.100.56。
55为redis master,56为redis slave,
监听默认的6379端口。

(1).创建Redis相关目录:
cd /usr/local/redis-3.0
# 如果相关目录已经在安装时,新建,下面步骤就忽略
mkdir -p /usr/local/redis-3.0/{data,log,etc}

(2).编辑Redis Master/Slave 相关配置文件:
vim etc/redis.conf

Master配置文件(生产实际配置文件不是这样,比较简单)
######################## GENERAL ######################
daemonize yes
pidfile /usr/local/redis-3.0/log/redis_6379.pid
port 6379
timeout 0
loglevel notice
logfile “/usr/local/redis-3.0/log/redis_6379.log”
syslog-enabled no
databases 16
#########################SAVE###########################
dbfilename dump.rdb
dir /usr/local/redis-3.0/data
stop-writes-on-bgsave-error no
#########################REPLICATION ###################
slave-serve-stale-data yes
slave-read-only yes
repl-timeout 60
repl-backlog-size 10mb
repl-backlog-ttl 3600
repl-ping-slave-period 10
min-slaves-to-write 0
min-slaves-max-lag 0
##########################SECURITY ######################
########################## LIMITS ########################
maxclients 2000
#########################APPEND ONLY MODE #############
appendonly no
appendfilename “appendonly.aof”
#########################SLOW LOG #######################
slowlog-log-slower-than 10000
slowlog-max-len 1000

**********************************************************************************

Slave配置文件(生产实际配置文件不是这样,比较简单)
######################## GENERAL ######################
daemonize yes
pidfile /usr/local/redis-3.0/log/redis_6379.pid
port 6379
timeout 0
loglevel notice
logfile “/usr/local/redis-3.0/log/redis.log”
syslog-enabled no
databases 16
#########################SAVE###########################
dbfilename dump.rdb
dir /usr/local/redis-3.0/data
stop-writes-on-bgsave-error no
#########################REPLICATION ###################
slaveof 172.16.10.55 6379
slave-serve-stale-data yes
slave-read-only yes
repl-timeout 60
repl-backlog-size 10mb
repl-backlog-ttl 3600
repl-ping-slave-period 10
min-slaves-to-write 0
min-slaves-max-lag 0
##########################SECURITY ######################
########################## LIMITS ########################
maxclients 2000
#########################APPEND ONLY MODE #############
appendonly no
appendfilename “appendonly.aof”
#########################SLOW LOG #######################
slowlog-log-slower-than 10000
slowlog-max-len 1000

(3).启动/关闭Redis服务:
启动Redis服务顺序:
先启动 Redis Master , 再启动 Redis Slave
关闭Redis服务顺序:
先关闭 Redis Master , 再关闭 Redis Slave

(4).验证Redis 主从复制是否正常:
最简单的方式,就是登录 Redis Master,设置一个值:select 0;set name Michael;
然后登录 Redis Slave ,看能不能获取到刚才设置的那个值:select 0;get name;

网上有种验证方式如下:
进入主从服务器的数据目录,查询对比所有服务器数据文件的散列值和文件大小:
find . -type f -name “*.rdb” | xargs md5sum
find . -type f -name “*.rdb” | xargs ls -l

生成报文摘要并验证,如果对比成功则数据已同步:
find . -type f -name “*.rdb” | xargs md5sum >biran
md5sum –check biran

Redis 3.0 编译安装

官方主站:http://www.redis.io/
下载地址:http://www.redis.cn/download.html
Command API: http://www.redis.cn/commands.html
Redis官方文档:http://redis.io/documentation

一.Redis 介绍:
Redis是Remote Dictionary Server的缩写。他本质上一个Key/Value数据库,与Memcached类似的NoSQL型数据库,但是他的数据可以持久化的保存在磁盘上,解决了服务重启后数据不丢失的问题,他的值可以是string(字符串)、list(列表)、sets(集合)或者是ordered sets(被排序的集合),所有的数据类型都具有push/pop、add/remove、执行服务端的并集、交集、两个sets集中的差别等等操作,这些操作都是具有原子性的,Redis还支持各种不同的排序能力。

二.Redis 部署
1.安装相关依赖包:
yum -y install gcc gcc-c++ cmake make

2.安装tcmalloc包[ 尝试采用新的内存分配方式 ]
Redis并没有自己实现内存池,没有在标准的系统内存分配器上再加上自己的东西。
所以系统内存分配器的性能及碎片率会对Redis造成一些性能上的影响。
在最新版本中,jemalloc已经作为源码包的一部分包含在源码包中,所以可以直接被使用。
如果要使用tcmalloc的话,是需要自己安装的。

tcmalloc是google-proftools( http://code.google.com/p/gperftools/downloads/list)中的一部分,
所以我们实际上需要安装google-proftools。如果你是在64位机器上进行安装,需要先安装其依赖的libunwind库,32位的系统不需要
下载地址: http://download.savannah.gnu.org/releases/libunwind/libunwind-0.99-alpha.tar.gz

(1).安装libunwind
tar zxvf libunwind-0.99-alpha.tar.gz
cd libunwind-0.99-alpha/
CFLAGS=-fPIC ./configure
make CFLAGS=-fPIC
make CFLAGS=-fPIC install

(2).安装gperftools
tar zxvf gperftools-2.1.tar.gz
cd gperftools-2.1
./configure –disable-cpu-profiler –disable-heap-profiler –disable-heap-checker –disable-debugalloc –enable-minimal
make && make install
cd /usr/local/lib
ln -sv libtcmalloc_minimal.so.4.1.2 libtcmalloc.so
# 加粗部分如果不做的话,后面编译redis会报错: usr/bin/ld: cannot find ltcmalloc
echo “/usr/local/lib” > /etc/ld.so.conf.d/usr_local_lib.conf #如果没有这个文件,自己建一个
/sbin/ldconfig

3.Redis 安装:
tar zxvf redis-3.0.0.tar.gz
cd redis-3.0.0
mkdir -p /usr/local/redis-3.0
# make prefix= install
# 将redis安装在/bin下/usr/local/redis-3.0/bin下
# make PREFIX=/usr/local/redis-3.0 install # 此方式安装,使用linux默认的内存分配方式
make PREFIX=/usr/local/redis-3.0 USE_TCMALLOC=yes FORCE_LIBC_MALLOC=yes install

4.建相关目录,并配置redis.conf 文件[简单配置]
cd /usr/local/redis-3.0/
mkdir etc #存放配置文件,这里目录名,可建为:conf
mkdir log #存放日志和pid文件
mkdir data #存放数据
vim etc/redis.conf
daemonize yes
port 6379
pidfile /usr/local/redis-3.0/log/redis_6379.pid
dir /usr/local/redis-3.0/data
logfile /usr/local/redis-3.0/log/redis_6379.log

5.Redis服务启动、关闭
(1).启动服务:
/usr/local/redis-3.0/bin/redis-server /usr/local/redis-3.0/etc/redis.conf
注:
此命令仅有一个启动参数,指定目录下的配置文件,不加参数执行默认配置。
测试启动:
/usr/local/redis-3.0/bin/redis-cli ping
返回PONG,则启动成功。
查看端口是否被占用:
netstat -ntlp |grep 6379

(2).关闭服务:
/usr/local/redis-3.0/bin/redis-cli shutdown
如果非默认端口,可指定端口:
/usr/local/redis-3.0/bin/redis-cli -p 6380 shutdown

(3).验证redis内存分配方式是否在使用tcmalloc
lsof -n |grep tcmalloc
redis-ser 2754 root mem REG 253,0 1078467 1725996 /usr/local/lib/libtcmalloc_minimal.so.4.1.2

6.Redis 服务启动脚本
redis 本身提供了redis安装脚本:utils/install_server.sh
但 这个脚本包括安装服务,有点烦,我们直接取其安装脚本:
#!/bin/sh
#Configurations injected by install_server below….
EXEC=/usr/local/redis-3.0/bin/redis-server
CLIEXEC=/usr/local/redis-3.0/bin/redis-cli
PIDFILE=/usr/local/redis-3.0/log/redis_6379.pid
CONF=”/usr/local/redis-3.0/etc/redis.conf”
REDISPORT=”6379″

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的复制流程

Redis的复制功能是基于内存快照的持久化策略基础上的,也就是说无论你的持久化策略选择的是什么,只要用到了Redis的复制功能,就一定会有内存快照发生,那么首先要注意你的系统内存容量规划

Redis复制流程在Slave和Master端各自是一套状态机流转,涉及的状态信息如下:
Slave 端:
REDIS_REPL_NONE
REDIS_REPL_CONNECT
REDIS_REPL_CONNECTED
Master端:

          REDIS_REPL_WAIT_BGSAVE_START
REDIS_REPL_WAIT_BGSAVE_END
REDIS_REPL_SEND_BULK
REDIS_REPL_ONLINE

整个状态机流程过程如下:
(1).Slave端在配置文件中添加了slave of指令,于是Slave启动时读取配置文件,初始状态为REDIS_REPL_CONNECT。
(2).Slave端在定时任务serverCron(Redis内部的定时器触发事件)中连接Master,发送sync命令,然后阻塞等待master发送回其内存快照文件(最新版的Redis已经不需要让Slave阻塞)。
     (3).Master端收到sync命令简单判断是否有正在进行的内存快照子进程,没有则立即开始内存快照,有则等待其结束,当快照完成后会将该文件发送给Slave端。
     (4).Slave端接收Master发来的内存快照文件,保存到本地,待接收完成后,清空内存表,重新读取Master发来的内存快照文件,重建整个内存表数据结构,并最终状态置位为 REDIS_REPL_CONNECTED状态,Slave状态机流转完成。
     (5).Master端在发送快照文件过程中,接收的任何会改变数据集的命令都会暂时先保存在Slave网络连接的发送缓存队列里(list数据结构),待快照完成后,依次发给Slave,之后收到的命令相同处理,并将状态置位为 REDIS_REPL_ONLINE。
整个复制过程完成,流程如下图所示:
 image1
Redis复制机制的缺陷

      从上面的流程可以看出,Slave从库在连接Master主库时,Master会进行内存快照,然后把整个快照文件发给Slave,
     也就是没有象MySQL那样有复制位置的概念,即无增量复制,这会给整个集群搭建带来非常多的问题。

比如一台线上正在运行的Master主库配置了一台从库进行简单读写分离,这时Slave由于网络或者其它原因与Master断开了连接,

     那么当Slave进行重新连接时,需要重新获取整个Master的内存快照,Slave所有数据跟着全部清除,
     然后重新建立整个内存表,一方面Slave恢复的时间会非常慢,另一方面也会给主库带来压力。
所以基于上述原因,如果你的Redis集群需要主从复制,那么最好事先配置好所有的从库,避免中途再去增加从库。

Cache还是Storage
在我们分析过了Redis的复制与持久化功能后,我们不难得出一个结论,实际上Redis目前发布的版本还都是一个单机版的思路,主要的问题集中在,持久化方式不够成熟,复制机制存在比较大的缺陷,这时我们又开始重新思考Redis的定位:Cache还是Storage?
如果作为Cache的话,似乎除了有些非常特殊的业务场景,必须要使用Redis的某种数据结构之外,我们使用Memcached可能更合适,毕竟Memcached无论客户端包和服务器本身更久经考验。
如果是作为存储Storage的话,我们面临的最大的问题是无论是持久化还是复制都没有办法解决Redis单点问题,即一台Redis挂掉了,没有太好的办法能够快速的恢复,通常几十G的持久化数据,Redis重启加载需要几个小时的时间,而复制又有缺陷,如何解决呢?

Redis可扩展集群搭建
1. 主动复制避开Redis复制缺陷。
既然Redis的复制功能有缺陷,那么我们不妨放弃Redis本身提供的复制功能,我们可以采用主动复制的方式来搭建我们的集群环境。
所谓主动复制是指由业务端或者通过代理中间件对Redis存储的数据进行双写或多写,通过数据的多份存储来达到与复制相同的目的,
               主动复制不仅限于用在Redis集群上,目前很多公司采用主动复制的技术来解决MySQL主从之间复制的延迟问题,
               比如Twitter还专门开发了用于复制和分区的中间件gizzard(https://github.com/twitter/gizzard) 。

主动复制虽然解决了被动复制的延迟问题,但也带来了新的问题,就是数据的一致性问题,数据写2次或多次,如何保证多份数据的一致性呢?

               如果你的应用对数据一致性要求不高,允许最终一致性的话,那么通常简单的解决方案是可以通过时间戳或者vector clock等方式,
               让客户端同时取到多份数据并进行校验,如果你的应用对数据一致性要求非常高,那么就需要引入一些复杂的一致性算法比如Paxos来保证数据的一致性,
               但是写入性能也会相应下降很多。

通过主动复制,数据多份存储我们也就不再担心Redis单点故障的问题了,如果一组Redis集群挂掉,我们可以让业务快速切换到另一组Redis上,降低业务风险。

2. 通过presharding进行Redis在线扩容。
通过主动复制我们解决了Redis单点故障问题,那么还有一个重要的问题需要解决:容量规划与在线扩容问题。
我们前面分析过Redis的适用场景是全部数据存储在内存中,而内存容量有限,那么首先需要根据业务数据量进行初步的容量规划,

                比如你的业务数据需要100G存储空间,假设服务器内存是48G,我们大约需要3~4台服务器来存储。
                这个实际是对现有业务情况所做的一个容量规划,假如业务增长很快,很快就会发现当前的容量已经不够了,Redis里面存储的数据很快就会超过物理内存大小,
                那么如何进行Redis的在线扩容呢?

Redis的作者提出了一种叫做presharding的方案来解决动态扩容和数据分区的问题,

     实际就是在同一台机器上部署多个Redis实例的方式,当容量不够时将多个实例拆分到不同的机器上,这样实际就达到了扩容的效果。

拆分过程如下:
在新机器上启动好对应端口的Redis实例。
配置新端口为待迁移端口的从库。
待复制完成,与主库完成同步后,切换所有客户端配置到新的从库的端口。
配置从库为新的主库。
移除老的端口实例。
重复上述过程迁移好所有的端口到指定服务器上。
     以上拆分流程是Redis作者提出的一个平滑迁移的过程,不过该拆分方法还是很依赖Redis本身的复制功能的,
     如果主库快照数据文件过大,这个复制的过程也会很久,同时会给主库带来压力。所以做这个拆分的过程最好选择为业务访问低峰时段进行。

Redis复制的改进思路
我们线上的系统使用了我们自己改进版的Redis,主要解决了Redis没有增量复制的缺陷,能够完成类似Mysql Binlog那样可以通过从库请求日志位置进行增量复制。
我们的持久化方案是首先写Redis的AOF文件,并对这个AOF文件按文件大小进行自动分割滚动,同时关闭Redis的Rewrite命令,
     然后会在业务低峰时间进行内存快照存储,并把当前的AOF文件位置一起写入到快照文件中,这样我们可以使快照文件与AOF文件的位置保持一致性,
     这样我们得到了系统某一时刻的内存快照,并且同时也能知道这一时刻对应的AOF文件的位置,那么当从库发送同步命令时,
     我们首先会把快照文件发送给从库,然后从库会取出该快照文件中存储的AOF文件位置,并将该位置发给主库,主库会随后发送该位置之后的所有命令,
     以后的复制就都是这个位置之后的增量信息了。
 image2
Redis与MySQL的结合
目前大部分互联网公司使用MySQL作为数据的主要持久化存储,那么如何让Redis与MySQL很好的结合在一起呢?
     我们主要使用了一种基于MySQL作为主库,Redis作为高速数据查询从库的异构读写分离的方案。

为此我们专门开发了自己的MySQL复制工具,可以方便的实时同步MySQL中的数据到Redis上。

 image3
(MySQL-Redis 异构读写分离)

总结:

Redis的复制功能没有增量复制,每次重连都会把主库整个内存快照发给从库,所以需要避免向在线服务的压力较大的主库上增加从库。
Redis的复制由于会使用快照持久化方式,所以如果你的Redis持久化方式选择的是日志追加方式(aof),

     那么系统有可能在同一时刻既做aof日志文件的同步刷写磁盘,又做快照写磁盘操作,这个时候Redis的响应能力会受到影响。
     所以如果选用aof持久化,则加从库需要更加谨慎。
可以使用主动复制和presharding方法进行Redis集群搭建与在线扩容。

Redis 3.0 新特性,支持redis 集群

Redis是一个开源、基于C语言、基于内存亦可持久化的高性能NoSQL数据库,同时,它还提供了多种语言的API。

近日,Redis 3.0在经过6个RC版本后,其正式版终于发布了。

Redis 3.0的最重要特征是对Redis集群的支持,此外,该版本相对于2.8版本在性能、稳定性等方面都有了重大提高。

一、Redis 3.0正式版相对于RC6版本的改进内容包括:

1.修复了无磁盘情况下的的复制问题;

2.在角色变化后对BLPOP复制进行了测试;

3.改进了prepareClientToWrite()错误处理;

4.移除了dict.c中不再使用的函数。

 

二、Redis 3.0版本相对于2.8版本带来的主要新特性包括:

1.实现了分布式的Redis即Redis Cluster,从而做到了对集群的支持;

2.引入了全新的”embedded string” 对象编码方式,从而实现了更少的缓存丢失和在特定的工作负载下速度的大幅提升;

3.AOF重写过程中的 “last write” 操作降低了AOF child -> parent数据传输的延迟

4.大幅提升LRU算法的性能以用于值的失效;

5.WAIT命令能够阻塞传输到指定数量从节点的写操作;

6.实现了对MIGRATE连接缓存的支持,从而大幅提升键值迁移的性能;

7.为MIGARTE新增了参数:COPY和REPLACE;

8.CLIENT PAUSE命令实现了在指定时间内停止处理客户端请求;

9.提高了BITCOUNT、INCR操作的性能;

10.CONFIG SET能够接受不同单位的内存值,如CONFIG SET maxmemory 1gb;

11.调整Redis日志格式。

 

文章转载自:开源中国

Redis配置文件(redis.conf)参数详解

redis配置文件参数说明:

1. Redis默认不是以守护进程的方式运行,可以通过该配置项修改,使用yes启用守护进程
daemonize no

2. 当Redis以守护进程方式运行时,Redis默认会把pid写入/var/run/redis.pid文件,可以通过pidfile指定
pidfile /var/run/redis.pid

3. 指定Redis监听端口,默认端口为6379,作者在自己的一篇博文中解释了为什么选用6379作为默认端口,因为6379在手机按键上MERZ对应的号码,而MERZ取自意大利歌女Alessia Merz的名字
port 6379

4. 绑定的主机地址
bind 127.0.0.1

5.当 客户端闲置多长时间后关闭连接,如果指定为0,表示关闭该功能
timeout 300

6. 指定日志记录级别,Redis总共支持四个级别:debug、verbose、notice、warning,默认为verbose
loglevel verbose

7. 日志记录方式,默认为标准输出,如果配置Redis为守护进程方式运行,而这里又配置为日志记录方式为标准输出,则日志将会发送给/dev/null
logfile stdout

8. 设置数据库的数量,默认数据库为0,可以使用SELECT 命令在连接上指定数据库id
databases 16

9. 指定在多长时间内,有多少次更新操作,就将数据同步到数据文件,可以多个条件配合
save

Redis默认配置文件中提供了三个条件:
save 900 1
save 300 10
save 60 10000
分别表示900秒(15分钟)内有1个更改,300秒(5分钟)内有10个更改以及60秒内有10000个更改。

10. 指定存储至本地数据库时是否压缩数据,默认为yes,Redis采用LZF压缩,如果为了节省CPU时间,可以关闭该选项,但会导致数据库文件变的巨大
rdbcompression yes

11. 指定本地数据库文件名,默认值为dump.rdb
dbfilename dump.rdb

12. 指定本地数据库存放目录
dir ./

13. 设置当本机为slav服务时,设置master服务的IP地址及端口,在Redis启动时,它会自动从master进行数据同步
slaveof

14. 当master服务设置了密码保护时,slav服务连接master的密码
masterauth

15. 设置Redis连接密码,如果配置了连接密码,客户端在连接Redis时需要通过AUTH 命令提供密码,默认关闭
requirepass foobared

16. 设置同一时间最大客户端连接数,默认无限制,Redis可以同时打开的客户端连接数为Redis进程可以打开的最大文件描述符数,如果设置 maxclients 0,表示不作限制。当客户端连接数到达限制时,Redis会关闭新的连接并向客户端返回max number of clients reached错误信息
maxclients 128

17. 指定Redis最大内存限制,Redis在启动时会把数据加载到内存中,达到最大内存后,Redis会先尝试清除已到期或即将到期的Key,当此方法处理 后,仍然到达最大内存设置,将无法再进行写入操作,但仍然可以进行读取操作。Redis新的vm机制,会把Key存放内存,Value会存放在swap区
maxmemory

18. 指定是否在每次更新操作后进行日志记录,Redis在默认情况下是异步的把数据写入磁盘,如果不开启,可能会在断电时导致一段时间内的数据丢失。因为 redis本身同步数据文件是按上面save条件来同步的,所以有的数据会在一段时间内只存在于内存中。默认为no
appendonly no

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

20. 指定更新日志条件,共有3个可选值:
no:表示等操作系统进行数据缓存同步到磁盘(快)
always:表示每次更新操作后手动调用fsync()将数据写到磁盘(慢,安全)
everysec:表示每秒同步一次(折衷,默认值)

appendfsync everysec

21. 指定是否启用虚拟内存机制,默认值为no,简单的介绍一下,VM机制将数据分页存放,由Redis将访问量较少的页即冷数据swap到磁盘上,访问多的页面由磁盘自动换出到内存中(在后面的文章我会仔细分析Redis的VM机制)
vm-enabled no

22. 虚拟内存文件路径,默认值为/tmp/redis.swap,不可多个Redis实例共享
vm-swap-file /tmp/redis.swap

23. 将所有大于vm-max-memory的数据存入虚拟内存,无论vm-max-memory设置多小,所有索引数据都是内存存储的(Redis的索引数据 就是keys),也就是说,当vm-max-memory设置为0的时候,其实是所有value都存在于磁盘。默认值为0
vm-max-memory 0

24. Redis swap文件分成了很多的page,一个对象可以保存在多个page上面,但一个page上不能被多个对象共享,vm-page-size是要根据存储的 数据大小来设定的,作者建议如果存储很多小对象,page大小最好设置为32或者64bytes;如果存储很大大对象,则可以使用更大的page,如果不 确定,就使用默认值
vm-page-size 32

25. 设置swap文件中的page数量,由于页表(一种表示页面空闲或使用的bitmap)是在放在内存中的,,在磁盘上每8个pages将消耗1byte的内存。
vm-pages 134217728

26. 设置访问swap文件的线程数,最好不要超过机器的核数,如果设置为0,那么所有对swap文件的操作都是串行的,可能会造成比较长时间的延迟。默认值为4
vm-max-threads 4

27. 设置在向客户端应答时,是否把较小的包合并为一个包发送,默认为开启
glueoutputbuf yes

28. 指定在超过一定的数量或者最大的元素超过某一临界值时,采用一种特殊的哈希算法
hash-max-zipmap-entries 64
hash-max-zipmap-value 512

29. 指定是否激活重置哈希,默认为开启(后面在介绍Redis的哈希算法时具体介绍)
activerehashing yes

30. 指定包含其它的配置文件,可以在同一主机上多个Redis实例之间使用同一份配置文件,而同时各个实例又拥有自己的特定配置文件
include /path/to/local.conf

31.是否将日志记录到syslog
syslog-enabled no

32.在配置启用save 参数后,在一个存盘点之后,可能磁盘会坏掉或者权限问题,下面参数设置为yes,redis 将依然正常工作

当持久化出现错误时,是否依然继续进行工作,是否终止所有的客户端write请求。默认设置”yes”表示终止,一旦snapshot数据保存故障,那么此server为只读服务。

如果为”no”,那么此次snapshot将失败,但下一次snapshot不会受到影响,不过如果出现故障,数据只能恢复到”最近一个成功点”

stop-writes-on-bgsave-error yes

33.当从主机脱离主的链接时,如果此值为yes,当客户端查询从时,会响应客户端,如果是第一次同步会返回一个日期数据或这空值,如果设置为no,则返回“SYNC with master in progress”到INFO and SLAVEOF
slave-serve-stale-data yes

34.从服务器只读(默认)
slave-read-only yes

35.从发送ping到主的时间间隔(单位:秒)
repl-ping-slave-period 10

36.批量传输I / O超时和主数据或ping响应超时 默认60s 必须大于repl-ping-slave-period值
repl-timeout 60

37.此选项如果是“yes”那么Redis的使用数量较少的TCP数据包和更少的带宽将数据发送到,在从主机上延迟40毫秒(linux kernel中的40毫秒)出现。

如果是no将在slave中减少延迟,但是流量使用回相对多一些,如果用多个从主机,此处建议设置成yes
repl-disable-tcp-nodelay no

38.从主机的优先级,如果当主主机挂了的时候,将从主机中选取一个作为其他从机的主,首先优先级的数字最低的将成为主,0是一个特殊的级别,0将永远不会成为主。默认值是100.
slave-priority 100

39.”慢操作日志”记录,单位:微秒(百万分之一秒,1000 * 1000),如果操作时间超过此值,将会把command信息”记录”起来.(内存,非文件)。

其中 “操作时间”不包括网络IO开支,只包括请求达到server后进行”内存实施”的时间.”0″表示记录全部操作
slowlog-log-slower-than 10000

40.”慢操作日志”保留的最大条数,”记录”将会被队列化,如果超过了此长度,旧记录将会被移除。

可以通过”SLOWLOG args”查看慢记录的信息(SLOWLOG get 10,SLOWLOG reset)
slowlog-max-len 1000