redis

redis-dump

yum install ruby rubygems ruby-devel
gem sources --add http://gems.ruby-china.org/ --remove http://rubygems.org/
gem sources -l
gem install redis-dump

redis-dump -u 127.0.0.1:6379 > data.json (导出redis 默认数据库的数据,默认数据库为0) 
如果指定15数据库的数据: 
redis-dump -u 127.0.0.1:6379 -d 15 > data.json
redis-load -u 192.168.1.3:19000 < data.json


aof导出
开启现有 Redis 实例的 AOF 功能(如果实例已经启用 AOF 功能则忽略此步骤)
# redis-cli -h ip -p port config set appendonly yes 

通过AOF文件将数据导入到新的 Redis 实例 (假定生成的 AOF 文件名为 append.aof)
# redis-cli -h ip -p 6379 -a pass --pipe < appendonly.aof

codis

wget https://github.com/CodisLabs/codis/releases/download/3.2.0/codis3.2.0-go1.7.5-linux.tar.gz   (二进制可执行文件)
https://github.com/CodisLabs/codis/archive/release3.2.zip (源码,包含脚本,配置文件)

需要升级glibc2.14

export LD_LIBRARY_PATH=/usr/local/glibc-2.14/lib/:$LD_LIBRARY_PATH

twemproxy代理

https://github.com/twitter/twemproxy


需要高版本的autoconf
wget http://ftp.gnu.org/gnu/autoconf/autoconf-2.69.tar.gz
tar -zxf autoconf-2.69.tar.gz 
cd autoconf-2.69
./configure --prefix=/usr/local/autoconf
make
make install

wget https://github.com/twitter/twemproxy/archive/master.zip
cd twemproxy-master/
/usr/local/autoconf/bin/autoreconf -fvi
./configure --prefix=/usr/local/twemproxy 
make -j 8
make install

cd /usr/local/twemproxy
mkdir run conf
vim /usr/local/twemproxy/conf/nutcracker.yml  参考官方配置
alpha:
  listen: 127.0.0.1:22121
  hash: fnv1a_64
  distribution: ketama
  auto_eject_hosts: true
  redis: true
  server_retry_timeout: 2000
  server_failure_limit: 1
  servers:
   - 127.0.0.1:7000:1
   - 127.0.0.1:7001:1

检查配置  ./sbin/nutcracker -t

启动  nutcracker -d -c /usr/local/twemproxy/conf/nutcracker.yml -p /usr/local/twemproxy/run/redisproxy.pid 
-o /usr/local/twemproxy/run/redisproxy.log

sentinel哨兵

配置redis主从
#从的redis配置文件,需要添加
vim redis.conf
slaveof 192.168.1.5 6379

分别启动主从  nohup ./src/redis-server redis.conf &

redis-cli -h 192.168.1.5 info Replication   主从信息
# Replication
role:master #代表192.168.9.18:6379 这台redis是主
connected_slaves:1
slave0:ip=192.168.1.6,port=6379,state=online,offset=29,lag=0

sentinel.conf 配置   (sentinel集群中各个sentinel也有互相通信,通过gossip协议)
port 26379
dir /tmp
#master1 
sentinel monitor master1 192.168.1.5 6379 2
#末尾的2代表,当集群中有2sentinel认为master死了时,才能真正认为该master已经不可用了,多个sentinel使用,单个sentinel配置为1 
sentinel down-after-milliseconds master1 10000
#sentinel会向master发送心跳PING来确认master是否存活   单位是毫秒
sentinel parallel-syncs master1 1
#在发生failover主备切换时,这个选项指定了最多可以有多少个slave同时对新的master进行同步,这个数字越小,
完成failover所需的时间就越长,但是如果这个数字越大,就意味着越多的slave因为replication而不可用。可以通过将这个值设为
 1 来保证每次只有一个slave处于不能处理命令请求的状态。
sentinel failover-timeout master1 180000
#failover超时时间,单位毫秒

#master2   可以添加多组主从的redis监听
....

./src/redis-sentinel sentinel.conf  启动
./src/redis-cli -h 192.168.1.6 -p 26379 info Sentinel   相关信息
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
master0:name=master1,status=ok,address=192.168.1.5:6379,slaves=1,sentinels=1

当主的redis 服务器岩机了,sentinel自动把从的redis切换到主
当之前的主redis回复后,sentinel 会把上次主redis重新加入服务中,但是他再以不是主的redis了,变成从的reids  
:相关信息可以从redis Sentinelinfo Sentinel 信息和日志查看

VIP漂移
VIP设置脚本

这个是在failover时执行的脚本。 
如下所示的参数会传递给脚本client-reconfig-script。
# The following arguments are passed to the script:
#
# <master-name> <role> <state> <from-ip> <from-port> <to-ip> <to-port>6个增加VIP,将成为一个Master,其它的则删除VIP。在failover时,仅仅使用ip命令可能会产生arp问题,
因此使用arping命令来抛出GRAP。在使用iparping命令时需要root权限,这里使用sudo来执行命令。

vim /var/lib/redis/failover.sh
chmod 755 /var/lib/redis/failover.sh
chown redis: /var/lib/redis/failover.sh
echo -e "redis\tALL=(ALL)\tNOPASSWD:/sbin/ip,NOPASSWD:/sbin/arping" > /etc/sudoers.d/redis
sed -i "s|Defaults.*requiretty|#Defaults\trequiretty|" /etc/sudoers
chmod 440 /etc/sudoers.d/redis

#!/bin/bash
MASTER_IP=${6}
MY_IP='192.168.0.1'   # 每个Server本身的IP
VIP='192.168.0.4'     # VIP
NETMASK='24'          # Netmask
INTERFACE='eth0'      # 接口

if [ ${MASTER_IP} = ${MY_IP} ]; then
        sudo /sbin/ip addr add ${VIP}/${NETMASK} dev ${INTERFACE}
        sudo /sbin/arping -q -c 3 -A ${VIP} -I ${INTERFACE}
        exit 0
else
        sudo /sbin/ip addr del ${VIP}/${NETMASK} dev ${INTERFACE}
        exit 0
fi
exit 1

Redis-Sentinel设置
开始设置redis-sentonel。 
你只需在第一次手工设置VIPvim /etc/redis-sentinel.conf
service redis-sentinel start
chkconfig redis-sentinel on
ip addr add 192.168.0.4/24 dev eth0

# sentinel.conf
port 26379
logfile /var/log/redis/sentinel.log
sentinel monitor mymaster 192.168.0.1 6379 2
sentinel down-after-milliseconds mymaster 3000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 60000
sentinel client-reconfig-script mymaster /var/lib/redis/failover.sh

结论
之后你可以尝试kill master而不是宕机来测试failover,我认为这是个比较好且容易实现的方法。 
sentinel down-after-milliseconds mymaster 3000 
大约在3秒左右会检测到redis的宕机。在更恶劣的环境,可以尝试减小这个值。

redis命令

tar zxf redis-3.0.2.tar.gz
cd redis-3.0.2
make
cd src && make install
cp ../redis.conf /etc

启动
redis-server > /dev/null & 或者 redis-server redis.conf 或者 redis-server /etc/redis.conf 1>log.log 2>errlog.log1为标准输出,2为错误输出)
停止 redis-cli shutdown

客户端连接 redis-cli

存值:
redis-cli set hx value
取值:
redis-cli get hx

> quit
退出连接

> dbsize
(integer) 12

当前数据库中key的数量

> info
服务器基本信息

monitor

实时转储收到的请求

config get
获取服务器的参数配置

flushdb
清空当前数据库

flushall
清除所有数据库



常规操作命令 

01  exits key              //测试指定key是否存在,返回1表示存在,0不存在 
02  del key1 key2 ....keyN //删除给定key,返回删除key的数目,0表示给定key都不存在 
03  type key               //返回给定keyvalue类型。返回 none 表示不存在key,string字符类型,list 链表类型 set 无序集合类型... 
04  keys pattern           //返回匹配指定模式的所有key,下面给个例子 
05  randomkey              //返回从当前数据库中随机选择的一个key,如果当前数据库是空的,返回空串 
06  rename oldkey newkey   //原子的重命名一个key,如果newkey存在,将会被覆盖,返回1表示成功,0失败。可能是oldkey不存在
或者和newkey相同 
07  renamenx oldkey newkey //同上,但是如果newkey存在返回失败 
08  dbsize                 //返回当前数据库的key数量 
09  expire key seconds     //key指定过期时间,单位是秒。返回1成功,0表示key已经设置过过期时间或者不存在 
10  ttl key                //返回设置过过期时间的key的剩余过期秒数 -1表示key不存在或者没有设置过过期时间 
11  select db-index        //通过索引选择数据库,默认连接的数据库所有是0,默认数据库数是16个。返回1表示成功,0失败 
12  move key db-index      //key从当前数据库移动到指定数据库。返回1成功。0 如果key不存在,或者已经在指定数据库中 
13  flushdb                //删除当前数据库中所有key,此方法不会失败。慎用 
14  flushall               //删除所有数据库中的所有key,此方法不会失败。更加慎用 
string 类型数据操作命令 

01  set key value         //设置key对应的值为string类型的value,返回1表示成功,0失败 
02  setnx key value       //同上,如果key已经存在,返回0nxnot exist的意思 
03  get key               //获取key对应的string值,如果key不存在返回nil 
04  getset key value      //原子的设置key的值,并返回key的旧值。如果key不存在返回nil 
05  mget key1 key2 ... keyN            //一次获取多个key的值,如果对应key不存在,则对应返回nil。下面是个实验,首先清空当
前数据库,然后设置k1,k2.获取时k3对应返回nil 
06  mset key1 value1 ... keyN valueN   //一次设置多个key的值,成功返回1表示所有的值都设置了,失败返回0表示没有任何值被设置 
07  msetnx key1 value1 ... keyN valueN //同上,但是不会覆盖已经存在的key 
08  incr key              //key的值做加加操作,并返回新的值。注意incr一个不是intvalue会返回错误,incr一个不存在的
key,则设置key1 
09  decr key              //同上,但是做的是减减操作,decr一个不存在key,则设置key-1 
10  incrby key integer    //incr,加指定值 ,key不存在时候会设置key,并认为原来的value0 
11  decrby key integer    //decr,减指定值。decrby完全是为了可读性,我们完全可以通过incrby一个负值来实现同样效果,反之一样。 
12  append key value      //给指定key的字符串值追加value,返回新字符串值的长度。下面给个例子 
13  substr key start end  //返回截取过的key的字符串值,注意并不修改key的值。下标是从0开始的,接着上面例子 
list 类型数据操作命令 

01  lpush key string          //key对应list的头部添加字符串元素,返回1表示成功,0表示key存在且不是list类型 
02  rpush key string          //同上,在尾部添加 
03  llen key                  //返回key对应list的长度,key不存在返回0,如果key对应类型不是list返回错误 
04  lrange key start end      //返回指定区间内的元素,下标从0开始,负值表示从后面计算,-1表示倒数第一个元素 ,key不存在返回空列表 
05  ltrim key start end       //截取list,保留指定区间内元素,成功返回1key不存在返回错误 
06  lset key index value      //设置list中指定下标的元素值,成功返回1key或者下标不存在返回错误 
07  lrem key count value      //key对应list中删除count个和value相同的元素。count0时候删除全部 
08  lpop key                  //list的头部删除元素,并返回删除元素。如果key对应list不存在或者是空返回nil,如果key对
应值不是list返回错误 
09  rpop                      //同上,但是从尾部删除 
10  blpop key1...keyN timeout //从左到右扫描返回对第一个非空list进行lpop操作并返回,比如blpop list1 list2 list3 0 ,
如果list不存在list2,list3都是非空则对list2lpop并返回从list2中删除的元素。如果所有的list都是空或不存在,则会阻塞timeout秒,
timeout0表示一直阻塞。当阻塞时,如果有clientkey1...keyN中的任意key进行push操作,则第一在这个key上被阻塞的client会立即返回。
如果超时发生,则返回nil。有点像unixselect或者poll 
11  brpop                     //blpop,一个是从头部删除一个是从尾部删除 
12  rpoplpush srckey destkey  //srckey对应list的尾部移除元素并添加到destkey对应list的头部,最后返回被移除的元素值,
整个操作是原子的.如果srckey是空或者不存在返回nil 
set 类型数据操作命令 

01  sadd key member                //添加一个string元素到,key对应的set集合中,成功返回1,如果元素以及在集合中返回0,key
对应的set不存在返回错误 
02  srem key member                //key对应set中移除给定元素,成功返回1,如果member在集合中不存在或者key不存在返回0,
如果key对应的不是set类型的值返回错误 
03  spop key                       //删除并返回key对应set中随机的一个元素,如果set是空或者key不存在返回nil 
04  srandmember key                //spop,随机取set中的一个元素,但是不删除元素 
05  smove srckey dstkey member     //srckey对应set中移除member并添加到dstkey对应set中,整个操作是原子的。成功返回1,
如果membersrckey中不存在返回0,如果key不是set类型返回错误 
06  scard key                      //返回set的元素个数,如果set是空或者key不存在返回0 
07  sismember key member           //判断member是否在set中,存在返回10表示不存在或者key不存在 
08  sinter key1 key2...keyN        //返回所有给定key的交集 
09  sinterstore dstkey key1...keyN //sinter,但是会同时将交集存到dstkey10  sunion key1 key2...keyN        //返回所有给定key的并集 
11  sunionstore dstkey key1...keyN //sunion,并同时保存并集到dstkey12  sdiff key1 key2...keyN         //返回所有给定key的差集 
13  sdiffstore dstkey key1...keyN  //sdiff,并同时保存差集到dstkey14  smembers key                   //返回key对应set的所有元素,结果是无序的 
sorted set 类型数据操作命令 

01  zadd key score member        //添加元素到集合,元素在集合中存在则更新对应score 
02  zrem key member              //删除指定元素,1表示成功,如果元素不存在返回0 
03  zincrby key incr member      //增加对应memberscore值,然后移动元素并保持skip list保持有序。返回更新后的score04  zrank key member             //返回指定元素在集合中的排名(下标),集合中元素是按score从小到大排序的 
05  zrevrank key member          //同上,但是集合中元素是按score从大到小排序 
06  zrange key start end         //类似lrange操作从集合中去指定区间的元素。返回的是有序结果 
07  zrevrange key start end      //同上,返回结果是按score逆序的 
08  zrangebyscore key min max    //返回集合中score在给定区间的元素 
09  zcount key min max           //返回集合中score在给定区间的数量 
10  zcard key                    //返回集合中元素个数 
11  zscore key element           //返回给定元素对应的score 
12  zremrangebyrank key min max  //删除集合中排名在给定区间的元素 
13  zremrangebyscore key min max //删除集合中score在给定区间的元素 
hash 类型数据操作命令 

01  hset key field value       //设置hash field为指定值,如果key不存在,则先创建 
02  hget key field             //获取指定的hash field 
03  hmget key filed1....fieldN //获取全部指定的hash filed 
04  hmset key filed1 value1 ... filedN valueN //同时设置hash的多个field 
05  hincrby key field integer  //将指定的hash filed 加上给定值 
06  hexists key field          //测试指定field是否存在 
07  hdel key field             //删除指定的hash field 
08  hlen key                   //返回指定hashfield数量 
09  hkeys key                  //返回hash的所有field 
10  hvals key                  //返回hash的所有value 
11  hgetall                    //返回hash的所有filedvalue 


redis.conf配置文件:
引用
#是否作为守护进程运行
daemonize yes
#配置pid的存放路径及文件名,默认为当前路径下
pidfile redis.pid
#Redis默认监听端口
port 6379
#客户端闲置多少秒后,断开连接
timeout 300
#日志显示级别
loglevel verbose
#指定日志输出的文件名,也可指定到标准输出端口
logfile stdout
#设置数据库的数量,默认连接的数据库是0,可以通过select N来连接不同的数据库
databases 16
#保存数据到disk的策略
#当有一条Keys数据被改变是,900秒刷新到disk一次
save 900 1
#当有10Keys数据被改变时,300秒刷新到disk一次
save 300 10
#当有1wkeys数据被改变时,60秒刷新到disk一次
save 60 10000
#当dump  .rdb数据库的时候是否压缩数据对象
rdbcompression yes
#dump数据库的数据保存的文件名
dbfilename dump.rdb
#Redis的工作目录
dir /home/falcon/redis-2.0.0/
###########  Replication #####################
#Redis的复制配置
# slaveof <masterip> <masterport>
# masterauth <master-password>

############## SECURITY ###########
# requirepass foobared

############### LIMITS ##############
#最大客户端连接数
# maxclients 128
#最大内存使用率
# maxmemory <bytes>

########## APPEND ONLY MODE #########
#是否开启日志功能
appendonly no
# 刷新日志到disk的规则
# appendfsync always
appendfsync everysec
# appendfsync no
################ VIRTUAL MEMORY ###########
#是否开启VM功能
vm-enabled no
# vm-enabled yes
vm-swap-file logs/redis.swap
vm-max-memory 0
vm-page-size 32
vm-pages 134217728
vm-max-threads 4
############# ADVANCED CONFIG ###############
glueoutputbuf yes
hash-max-zipmap-entries 64
hash-max-zipmap-value 512
#是否重置Hashactiverehashing yes


spring中整合redis
看了java Clientsredis官网比较推荐Jedis,而springredis的客服端做了一个统一封装,支持(Jedis,  JRedis, and RJC),
这里对说下SPRING DATA - REDIS1、使用maven的话那就很简单了,直接加入依赖。

2、编辑pom.xml
<repository>   
 <id>spring-milestone</id>   
 <name>Spring Maven MILESTONE Repository</name>   
 <url>http://maven.springframework.org/milestone</url>   
</repository>   

<dependency>   
 <groupId>org.springframework.data</groupId>   
 <artifactId>spring-data-redis</artifactId>   
 <version>1.0.0.RC1</version>   
</dependency>

spring配置文件里添加
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">   
        <property name="hostName" value="localhost"/>   
        <property name="port" value="6636"/>   
</bean>   

<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">   
        <property name="connectionFactory" ref="jedisConnectionFactory"/>   
</bean>   

Java代码
@Service   
public class RedisService {   
    @Resource   
    private RedisTemplate<Serializable, Serializable> template;   

 /**  
     * 向redis里面添加key-value格式的数据  
     *  
     * @param key   key  
     * @param value value  
     */   

    public void set(final Serializable key, final Serializable value) {   
        template.execute(new RedisCallback<Object>() {   
            @Override   
            public Object doInRedis(RedisConnection connection) throws DataAccessException {   
                byte[] key_ = RedisUtil.getBytesFromObject(key);   
                byte[] value_ = RedisUtil.getBytesFromObject(value);   
                connection.set(key_, value_);   
                return true;   
            }   
        });   
    }   

 /**  
     * 根据key从redis里面取出value  
     *  
     * @param key   key  
     */   
 public Serializable get(final Serializable key) {   
        return template.execute(new RedisCallback<Serializable>() {   
            @Override   
            public Serializable doInRedis(RedisConnection connection) throws DataAccessException {   

                byte[] keyBytes = RedisUtil.getBytesFromObject(key);   
                byte[] bytes = connection.get(keyBytes);   
                return (Serializable) RedisUtil.getObjectFromBytes(bytes);   
            }   
        });   
    }   
}   

看了一点JedisConnectionFactory,之际上它只是对Jedis做了下简单了封装,再加上自己的连接池实现。

web管理

https://github.com/erikdubbelboer/phpRedisAdmin
https://github.com/nrk/predis

cd  /var/www/html/
wget https://github.com/erikdubbelboer/phpRedisAdmin/archive/master.zip
unzip master.zip
rm -f master.zip
cd phpRedisAdmin
wget  https://github.com/nrk/predis/archive/v1.1.zip   解压放到上面包里,重命名为vendor
unzip v1.1.zip
rm -f v1.1.zip
mv predis-1.1  vendor

vim  phpRedisAdmin/includes/config.sample.inc.php   配置连接的redis地址,(可以配置从库,这样就是个只读的redis)

如果确少php驱动,可以安装
yum search redis | grep php
php-nrk-Predis.noarch : PHP client library for Redis
php-pecl-redis.x86_64 : Extension for communicating with the Redis key-value
php-redis.x86_64 : Extension for communicating with the Redis key-value store

http://ip/phpredis/ 

redis cluster

一:关于redis cluster
1:redis cluster的现状
reids-cluster计划在redis3.0中推出,可以看作者antirez的声明:http://antirez.com/news/49 (ps:跳票了好久,今年貌似加快速度了),
目前的最新版本是redis3 beta2(2.9.51).
作者的目标:Redis Cluster will support up to ~1000 nodes. 赞...
目前redis支持的cluster特性(已亲测):
1):节点自动发现
2):slave->master 选举,集群容错
3):Hot resharding:在线分片
4):进群管理:cluster xxx
5):基于配置(nodes-port.conf)的集群管理
6):ASK 转向/MOVED 转向机制.
2:redis cluster 架构
1)redis-cluster架构图

架构细节:
(1)所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽.
(2)节点的fail是通过集群中超过半数的节点检测失效时才生效.
(3)客户端与redis节点直连,不需要中间proxy层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可
(4)redis-cluster把所有的物理节点映射到[0-16383]slot上,cluster 负责维护node<->slot<->value
2) redis-cluster选举:容错

(1)领着选举过程是集群中所有master参与,如果半数以上master节点与master节点通信超过(cluster-node-timeout),认为当前master节点挂掉.
(2):什么时候整个集群不可用(cluster_state:fail),当集群不可用时,所有对集群的操作做都不可用,收到((error) CLUSTERDOWN
 The cluster is down)错误
    a:如果集群任意master挂掉,且当前master没有slave.集群进入fail状态,也可以理解成进群的slot映射[0-16383]不完成时进入fail状态.
    b:如果进群超过半数以上master挂掉,无论是否有slave集群进入fail状态.
二:redis cluster的使用
1:安装redis cluster
1):安装redis-cluster依赖:redis-cluster的依赖库在使用时有兼容问题,在reshard时会遇到各种错误,请按指定版本安装.
(1)确保系统安装zlib,否则gem install会报(no such file to load -- zlib)

#download:zlib-1.2.6.tar  ./configure  make  make install


 (1)安装ruby:version(1.9.2)
# ruby1.9.2   cd /path/ruby  ./configure -prefix=/usr/local/ruby  make  make install  sudo cp ruby /usr/local/bin
(2)安装rubygem:version(1.8.16)

# rubygems-1.8.16.tgz  cd /path/gem  sudo ruby setup.rb  sudo cp bin/gem /usr/local/bin
(3)安装gem-redis:version(3.0.0)

gem install redis --version 3.0.0  #由于源的原因,换成阿里的源https://ruby.taobao.org/ $ gem sources --add 
https://ruby.taobao.org/ --remove https://rubygems.org/$ gem sources -l*** CURRENT SOURCES ***
 https://ruby.taobao.org# 请确保只有 ruby.taobao.org$ gem install rails
2)安装redis-cluster
cd /path/redis  make  sudo cp /opt/redis/src/redis-server /usr/local/bin  sudo cp /opt/redis/src/redis-cli 
/usr/local/bin  sudo cp /opt/redis/src/redis-trib.rb /usr/local/bin
2:配置redis cluster
1)redis配置文件结构:

 使用包含(include)把通用配置和特殊配置分离,方便维护.
2)redis通用配置.

#GENERAL  daemonize no  tcp-backlog 511  timeout 0  tcp-keepalive 0  loglevel notice  databases 16  
dir /opt/redis/data  slave-serve-stale-data yes  #slave只读  slave-read-only yes  #not use default  
repl-disable-tcp-nodelay yes  slave-priority 100  #打开aof持久化  appendonly yes  #每秒一次aofappendfsync everysec  #关闭在aof rewrite的时候对新的写操作进行fsync  no-appendfsync-on-rewrite yes  
auto-aof-rewrite-min-size 64mb  lua-time-limit 5000  #打开redis集群  cluster-enabled yes  
#节点互连超时的阀值  cluster-node-timeout 15000  cluster-migration-barrier 1  slowlog-log-slower-than 10000  
slowlog-max-len 128  notify-keyspace-events ""  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
3)redis特殊配置.

#包含通用配置  include /opt/redis/redis-common.conf  #监听tcp端口  port 6379  #最大可用内存  maxmemory 100m  
#内存耗尽时采用的淘汰策略:  # volatile-lru -> remove the key with an expire set using an LRU algorithm  
# allkeys-lru -> remove any key accordingly to the LRU algorithm  # volatile-random -> remove a random 
key with an expire set  # allkeys-random -> remove a random key, any key  # volatile-ttl -> remove the key
 with the nearest expire time (minor TTL)  # noeviction -> don't expire at all, just return an error on
  write operations  maxmemory-policy allkeys-lru  #aof存储文件  appendfilename "appendonly-6379.aof"  
  #rdb文件,只用于动态添加slave过程  dbfilename dump-6379.rdb  #cluster配置文件(启动自动生成)  
  cluster-config-file nodes-6379.conf  #部署在同一机器的redis实例,把<span style="font-size: 1em; 
  line-height: 1.5;">auto-aof-rewrite搓开,防止瞬间fork所有redis进程做rewrite,占用大量内存</span>  
  auto-aof-rewrite-percentage 80-100
3:cluster 操作
cluster集群相关命令,更多redis相关命令见文档:http://redis.readthedocs.org/en/latest/
集群  CLUSTER INFO 打印集群的信息  CLUSTER NODES 列出集群当前已知的所有节点(node),以及这些节点的相关信息。  
节点  CLUSTER MEET <ip> <port>ipport 所指定的节点添加到集群当中,让它成为集群的一份子。  CLUSTER FORGET <node_id> 
从集群中移除 node_id 指定的节点。  CLUSTER REPLICATE <node_id> 将当前节点设置为 node_id 指定的节点的从节点。 
 CLUSTER SAVECONFIG 将节点的配置文件保存到硬盘里面。  槽(slot)  CLUSTER ADDSLOTS <slot> [slot ...] 将一个或多
 个槽(slot)指派(assign)给当前节点。  CLUSTER DELSLOTS <slot> [slot ...] 移除一个或多个槽对当前节点的指派。
   CLUSTER FLUSHSLOTS 移除指派给当前节点的所有槽,让当前节点变成一个没有指派任何槽的节点。 
    CLUSTER SETSLOT <slot> NODE <node_id> 将槽 slot 指派给 node_id 指定的节点,如果槽已经指派给另一个节点,
    那么先让另一个节点删除该槽>,然后再进行指派。  CLUSTER SETSLOT <slot> MIGRATING <node_id> 将本节点的槽
     slot 迁移到 node_id 指定的节点中。  CLUSTER SETSLOT <slot> IMPORTING <node_id>node_id 指定的节点
     中导入槽 slot 到本节点。  CLUSTER SETSLOT <slot> STABLE 取消对槽 slot 的导入(import)或者迁移(migrate)。  
     键  CLUSTER KEYSLOT <key> 计算键 key 应该被放置在哪个槽上。  CLUSTER COUNTKEYSINSLOT <slot> 
     返回槽 slot 目前包含的键值对数量。  CLUSTER GETKEYSINSLOT <slot> <count> 返回 countslot 槽中的键。
4:redis cluster 运维操作
1)初始化并构建集群
(1)#启动集群相关节点(必须是空节点),指定配置文件和输出日志

redis-server /opt/redis/conf/redis-6380.conf > /opt/redis/logs/redis-6380.log 2>&1 & redis-server
 /opt/redis/conf/redis-6381.conf > /opt/redis/logs/redis-6381.log 2>&1 & redis-server /opt/redis/conf/redis-6382.conf
> /opt/redis/logs/redis-6382.log 2>&1 & redis-server /opt/redis/conf/redis-7380.conf > /opt/redis/logs/redis-7380.log
   2>&1 & redis-server /opt/redis/conf/redis-7381.conf > /opt/redis/logs/redis-7381.log 2>&1 & redis-server 
   /opt/redis/conf/redis-7382.conf > /opt/redis/logs/redis-7382.log 2>&1 &
(2):使用自带的ruby工具(redis-trib.rb)构建集群

#redis-trib.rbcreate子命令构建  #--replicas 则指定了为Redis Cluster中的每个Master节点配备几个Slave节点  
#节点角色由顺序决定,先master之后是slave(为方便辨认,slave的端口比master1000)  redis-trib.rb create --replicas 
1 10.10.34.14:6380 10.10.34.14:6381 10.10.34.14:6382 10.10.34.14:7380 10.10.34.14:7381 10.10.34.14:7382
(3):检查集群状态,

#redis-trib.rbcheck子命令构建  #ip:port可以是集群的任意节点  redis-trib.rb check 1 10.10.34.14:6380
最后输出如下信息,没有任何警告或错误,表示集群启动成功并处于ok状态
 [OK] All nodes agree about slots configuration. 
>>> Check for open slots... 
>>> Check slots coverage... 
[OK] All 16384 slots covered. 

redis-cli -c -p 6379  ( -c  集群模式)

2):添加新master节点
(1)添加一个master节点:创建一个空节点(empty node),然后将某些slot移动到这个空节点上,这个过程目前需要人工干预
a):根据端口生成配置文件(ps:establish_config.sh是我自己写的输出配置脚本)
 sh establish_config.sh 6386 > conf/redis-6386.conf  
b):启动节点
 nohup redis-server /opt/redis/conf/redis-6386.conf > /opt/redis/logs/redis-6386.log 2>&1 & 
c):加入空节点到集群
add-node  将一个节点添加到集群里面, 第一个是新节点ip:port, 第二个是任意一个已存在节点ip:port
redis-trib.rb add-node 10.10.34.14:6386 10.10.34.14:6381   
node:新节点没有包含任何数据, 因为它没有包含任何slot。新加入的加点是一个主节点, 当集群需要将某个从节点升级为新的主节点时,
 这个新节点不会被选中
d):为新节点分配slot
redis-trib.rb reshard 10.10.34.14:6386  #根据提示选择要迁移的slot数量(ps:这里选择500)  How many slots 
do you want to move (from 1 to 16384)? 500  #选择要接受这些slotnode-id  What is the receiving node ID?
 f51e26b5d5ff74f85341f06f28f125b7254e61bf  #选择slot来源:  #all表示从所有的master重新分配,  #或者数据要提取
 slotmaster节点id,最后用done结束  Please enter all the source node IDs.    Type 'all' to use all the 
 nodes as source nodes for the hash slots.    Type 'done' once you entered all the source nodes IDs. 
  Source node #1:all  #打印被移动的slot后,输入yes开始移动slot以及对应的数据.  #Do you want to proceed with
   the proposed reshard plan (yes/no)? yes  #结束
3):添加新的slave节点
a):前三步操作同添加master一样
b)第四步:redis-cli连接上新节点shell,输入命令:cluster replicate 对应masternode-id
 cluster replicate 2b9ebcbd627ff0fd7a7bbcc5332fb09e72788835  
note:在线添加slave 时,需要dump整个master进程,并传递到slave,再由 slave加载rdb文件到内存,rdb传输过程中Master
可能无法提供服务,整个过程消耗大量io,小心操作.
例如本次添加slave操作产生的rdb文件
 -rw-r--r-- 1 root root  34946 Apr 17 18:23 dump-6386.rdb 
-rw-r--r-- 1 root root  34946 Apr 17 18:23 dump-7386.rdb 
4):在线reshard 数据:
对于负载/数据均匀的情况,可以在线reshard slot来解决,方法与添加新masterreshard一样,只是需要reshardmaster节点是老节点.
5):删除一个slave节点

#redis-trib del-node ip:port '<node-id>' redis-trib.rb del-node 10.10.34.14:7386 
'c7ee2fca17cb79fe3c9822ced1d4f6c5e169e378'

a):删除master节点之前首先要使用reshard移除master的全部slot,然后再删除当前节点(目前只能把被删除
masterslot迁移到一个节点上)
#把10.10.34.14:6386当前master迁移到10.10.34.14:6380redis-trib.rb reshard 10.10.34.14:6380  
#根据提示选择要迁移的slot数量(ps:这里选择500)  How many slots do you want to move (from 1 to 16384)? 
500(被删除master的所有slot数量)  #选择要接受这些slotnode-id(10.10.34.14:6380)  What is the receiving 
node ID? c4a31c852f81686f6ed8bcd6d1b13accdc947fd2 (ps:10.10.34.14:6380node-id)  Please enter all the
 source node IDs.    Type 'all' to use all the nodes as source nodes for the hash slots.    Type 'done' 
 once you entered all the source nodes IDs.  Source node #1:f51e26b5d5ff74f85341f06f28f125b7254e61bf
 (被删除masternode-id)  Source node #2:done  #打印被移动的slot后,输入yes开始移动slot以及对应的数据.  
 #Do you want to proceed with the proposed reshard plan (yes/no)? yes
b):删除空master节点
redis-trib.rb del-node 10.10.34.14:6386 'f51e26b5d5ff74f85341f06f28f125b7254e61bf'   

redis-monitor

https://github.com/LittlePeng/redis-monitor

RDB和AOF

RDB和AOF
RDB方式按照一定的时间间隔对数据集创建基于时间点的快照
AOF方式记录Server收到的写操作到日志文件在Server重启时通过回放这些写操作来重建数据集该方式类似于MySQL中基于语句格式的
binlog当日志变大时Redis可在后台重写日志
若仅期望数据在Server运行期间存在则可禁用两种持久化方案在同一Redis实例中同时开启AOF和RDB方式的数据持久化方案也是可以的
该情况下Redis重启时AOF文件将用于重建原始数据集因为叫RDB方式而言AOF方式能最大限度的保证数据完整性

两钟方案各自的优缺点
RDB优点
RDB是Redis数据集的基于时间点的紧凑的副本非常适合于备份场景比如每个小时对RDB文件做一次小的归档每天对RDB文件做一次大的
归档每月对RDB文件做一次更大的归档这样可以在必要的时刻选择不同的备份版本进行数据恢复
由于是一个紧凑的文件易于传输到远程数据中心或Amazon S3因此RDB非常适合于灾难恢复
RDB方式的开销较低在该种方式下Redis父进程所要做的仅是开辟一个子进程来做剩下的事情
与AOF相比RDB在数据集较大时能够以更快的速度恢复

RDB缺点
若需在Redis停止工作时例如意外断电尽可能保证数据不丢失那么RDB不是最好的方案例如通常会每隔5分钟或者更长的时间来创建
一次快照如若Redis没有被正确的关闭就可能丢失最近几分钟的数据
RDB方式需经常调用fork()函数以开辟子进程来实现持久化在数据集较大CPU性能不够强悍时fork()调用可能很耗时从而会导致Re
dis在几毫秒甚至一秒中的时间内不能服务clientsAOF也需要调用fork()但却可以在不影响数据持久性的条件下调整重写logs的频率

AOF优点
使用AOF方式时Redis持久化更可靠有三种不同的fsync策略供选择no fsync at allfsync every second fsync at every query
默认为fsync every second此时的写性能仍然很好且最坏的情况下可能丢失一秒钟的写操作
AOF日志是append only方式产生的日志因此不存在随机访问问题以及意外断电时造成的损毁问题即使出于某种原因如磁盘满日志以一个
写了一半的命令结尾仍可以使用redis-check-aof工具快速进行修复
当AOF日志逐渐变大后Redis可在后台自动的重写AOF日志当Redis在继续追加旧的AOF日志文件时重写日志是完全安全的Redis利用可以重
建当前数据集的最少的命令产生一个全新的日志文件一旦新的日志文件创建完成Redis开始向新的日志文件追加日志
AOF日志的格式易于理解易于解析这在某些场景非常有用比如不下心使用FLUSHALL命令清空了所有的数据同时AOF日志没有发生重写操作
那么就可以简单的通过停止Redis Server移除日志中的最后一条FLUSHALL命令重启Redis Server来恢复数据

AOF缺点
同样的数据集AOF文件要比RDB文件大很多
根据使用的fsync方式不同AOF可能比RDB慢很多在使用no fsync at all时AOF的性能基本与RDB持平在使用fsync every second时性能有
所下降但仍然较高在使用 fsync at every query时性能较低然而RDB方式却能在高负载的情况下保证延迟尽可能小
一些特定的命令可能存在bug从而导致重载AOF日志时不能重建出完全一样的数据集这样的bugs非常非常罕见已经通过测试套件做了充分的测
这种类型的bugs对于RDB来说几乎是不可能的说的更清晰一点Redis AOF增量的更新既存的状态而RDB快照每次都重新创建从概念上讲
RDB方式更加健壮然而需要注意两点每次AOF日志被Redis重写的时候日志由包含数据集的实际数据重新生成与追加AOF文件的方式相比该
方式能有效减少bugs出现的概率现实的应用场景中还未收到过任何用户关于AOF损毁的报告

如何选择持久化方式
取决于具体的应用场景通常两种方式可同时使用若比较关心数据但仍能忍受几分钟的数据丢失那么可以简单的使用RDB方式有许多用户
只使用AOF方式不建议这种做法一方面以一定时间间隔创建RDB快照是创建数据备份并快速恢复数据的极好的办法一方面可以避免AOF方式
可能存在的bugs出于上述原因将来可能将AOF和RDB方式合二为一

RDB持久化设置
默认情况下Redis在磁盘上创建二进制格式的命名为dump.rdb的数据快照可以通过配置文件配置每隔N秒且数据集上至少有M个变化时创建快照
是否对数据进行压缩快照名称存放快照的工作目录redis 2.4.10的默认配置如下
[plain] view plain copy 在CODE上查看代码片派生到我的代码片
#900秒后且至少1个key发生变化时创建快照 
save 900 1 
#300秒后且至少10个key发生变化时创建快照 
save 300 10 
#60秒后且至少10000个key发生变化时创建快照 
save 60 10000 
#可通过注释所有save开头的行来禁用RDB持久化 
#创建快照时对数据进行压缩 
rdbcompression yes 
#快照名称 
dbfilename dump.rdb 
#存放快照的目录AOF文件也会被存放在此目录 
dir /var/lib/redis/ 
关于配置参数的详细信息可参阅redis.conf中的说明

除了通过配置文件进行设置外也可以通过手工执行命令来创建快照
SAVE命令执行一个同步操作以RDB文件的方式保存实例中所有数据的快照一般不在生产环境直接使用SAVE 命令因为会阻塞所有的
客户端的请求可以使用BGSAVE命令代替BGSAVE后台创建数据快照命名执行结果的状态码会立即返回Redis开辟一个子进程父进程
继续相应客户端请求子进程保存DB到磁盘后退出客户端可通过执行LASTSAVE命令检查操作是否成功

创建RDB快照的工作流程
Redis需dump数据集到磁盘时会执行下列过程
Redis forks一个子进程
子进程写数据集到临时的RDB文件
子进程写完新的RDB文件后替换旧的RDB文件
该方式使Redis可以利用copy-on-write机制的好处

AOF持久化设置
利用快照的持久化方式不是非常可靠当运行Redis的计算机停止工作意外掉电意外杀掉了Redis进程那么最近写入Redis的数据将会


对于某些应用这或许不成问题但对于持久化要求非常高的应用场景快照方式不是理想的选择AOF文件是一个替代方案用以最大限度
的持久化数据同样可以通过配置文件来开闭AOF
[plain] view plain copy 在CODE上查看代码片派生到我的代码片
#关闭AOF 
appendonly no 
#打开AOF 
appendonly yes 
当设置appendonly为yes后每次Redis接收到的改变数据集的命令都会被追加到AOF文件重启Redis后会重放AOF文件来重建数据
还可以通过配置文件配置AOF文件名调用fsync的频率调用fsync的行为重写AOF的条件redis 2.4.10的默认配置如下
[plain] view plain copy 在CODE上查看代码片派生到我的代码片
#默认AOF文件名 
appendfilename appendonly.aof 
#每秒调用一次fsync刷新数据到磁盘 
appendfsync everysec 
#当进程中BGSAVE或BGREWRITEAOF命令正在执行时不阻止主进程中的fsync()调用默认为no当存在延迟问题时需调整为yes 
no-appendfsync-on-rewrite no 
#当AOF增长率为100%且达到了64mb时开始自动重写AOF 
auto-aof-rewrite-percentage 100 
auto-aof-rewrite-min-size 64mb 
各参数含义可参阅redis.conf中详细说明

几点说明
日志重写
随着Redis接收到的命令的增加AOF文件会变得越来越大Redis支持日志重写特性可以在不影响响应客户端的前提下在后台重构AOF文
当在Redis中执行BGREWRITEAOF后Redis将使用构建数据集所需的最少的命令来重构日志文件Redis2.2中需要经常手动运行
BGREWRITEAOF,Redis2.2开始支持自动触发日志重写

日志重写同样使用copy-on-write机制流程大致如下
Redis开辟一个子进程
子进程在临时文件中写新的AOF文件
父进程将所有新的更改缓存在memory中同时新更改被写入旧的AOF这样即使重写操作失败了也是安全的);
在子进程重写好临时AOF后父进程收到一个信号并追加memory中缓冲的更改到子进程产生的临时文件的末尾
Redis进行文件重命名用新的文件替换旧的文件并开始追加新的数据到新文件

fsync调用模式
该模式决定了Redis刷新数据到磁盘的频率有三个可选项
no fsync at all 全由操作系统决定刷数据的时机最快但最不安全
fsync every second 每秒一次刷新足够快最多可丢失一秒的数据
fsync at every query 每次记录一条新的命令到AOF便刷一次数据到磁盘最慢但最安全
默认策略也是默认策略为fsync every second

AOF损坏时的对策
若在写AOF文件时Server崩溃则可能导致AOF文件损坏而不能被Redis载入可通过如下步骤修复
创建一个AOF文件的备份
使用redis-check-aof工具修复原始的AOF文件
$ redis-check-aof --fix
使用diff -u 检查备份文件和修复后文件的异同可选步骤);
使用修复后的AOF文件重启Redis

如何由RDB持久化转换到AOF持久化
Redis >=2.2
创建最近的RDB文件的备份
将备份保存在安全的位置
发起如下命令
$redis-cli config set appendonly yes
$redis-cli config set save ""可选若不执行RDB和AOF方式将并存
确认数据库包含相同的keys
确认write操作被正确追加到了AOF文件
注意事项记得修改redis.conf中对应的配置以免Redis Server重启后通过命令进行的配置更新丢失而重新使用旧的配置文件中配置

Redis2.0
创建最近的RDB文件的备份
将备份存放在安全的位置
停止数据库上的所有写操作
发起 redis-cli bgrewriteaof命令创建AOF文件
当AOF文件生成后停止Redis Server
编辑redis.conf开启AOF持久化
重启Redis Server
确认数据库包含相同的keys
确认write操作被正确追加到了AOF文件

AOF与RDB之间的相互影响
Redis2.4以上的版本会确保在RDB快照创建时不触发AOF重写或者在AOF重写时不允许BGSAVE操作以避免Redis后台进程同时做繁重
的磁盘I/O操作
当创建RDB快照时对于用户使用BGREWRITEAOF明确发起的日志重写操作server会立刻回应一个ok状态码告知用户操作将回被执行当且
仅当快照创建完成后重写操作开始被执行
在同时使用了AOF和RDB方式的情况下Redis重启后会优先使用AOF文件来重构原始数据集

备份Redis 数据
务必做好数据备份以防意外丢失Redis是备份友好的可在数据库运行时拷贝RDB文件建议的备份方案
创建一个cron作业在一个目录中每小时创建一次RDB快照在另一目录中每天创建一次RDB快照
cron作业每次运行的时候使用find命令确保过时的RDB快照文件被清理掉可以通过在快照命中包含数据和时间信息来进行标记);
确保将RDB快照转移到外部的数据中心或者至少是运行Redis实例的物理机之外的机器至少每天一次)。

灾难恢复
在Redis中灾难恢复和数据备份基本上是同样的过程可考虑将备份分布到不同的远程数据中心以最大限度的避免数据丢失几种低成
本的灾难恢复计划
Amazon S3或其它类似服务是很好的选择可将每天会每小时的RDB快照以加密的方式可使用gpg -c加密传输到S3确保将密码存
储在不同的安全的地方建议使用不同的存储服务以提高数据安全性
使用SCP命令将快照传输到远程服务器最简单和安全的方式获取一个小的远程VPS在其上安装ssh生成无密码的ssh client 
key添加到VPS的authorized_keys文件此后便可使用SCP传输备份到VPS了建议搞两个不同的VPS以提高安全性

需要注意的是文件传输完成后一定要校验文件的完整性正确性可通过MD5或SHA1进行验证另外需要搭建一套告警系统
备份传输发生问题时能及时的告知

Redis的复制及集群搭建

Redis的复制及Redis优缺点和集群搭建
Redis复制流程概述
Redis的复制功能是完全建立在之前我们讨论过的基于内存快照的持久化策略基础上的,也就是说无论你的持久化策略选择的是什么,
只要用到了 Redis的复制功能,就一定会有内存快照发生,那么首先要注意你的系统内存容量规划,原因可以参考我上一篇文章中提到
Redis磁盘IO问题。
Redis复制流程在SlaveMaster端各自是一套状态机流转,涉及的状态信息是:
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
整个状态机流程过程如下:
Slave端在配置文件中添加了slave of指令,于是Slave启动时读取配置文件,初始状态为REDIS_REPL_CONNECT
Slave端在定时任务serverCron(Redis内部的定时器触发事件)中连接Master,发送sync命令,然后阻塞等待master发送回其内存
快照文件(最新版的Redis已经不需要让Slave阻塞)
Master端收到sync命令简单判断是否有正在进行的内存快照子进程,没有则立即开始内存快照,有则等待其结束,当快照完成后会
将该文件发送给Slave端。
Slave端接收Master发来的内存快照文件,保存到本地,待接收完成后,清空内存表,重新读取Master发来的内存快照文件,重建整
个内存表数据结构,并最终状态置位为 REDIS_REPL_CONNECTED状态,Slave状态机流转完成。
Master端在发送快照文件过程中,接收的任何会改变数据集的命令都会暂时先保存在Slave网络连接的发送缓存队列里(list数据结构),
待快照完成后,依次发给Slave,之后收到的命令相同处理,并将状态置位为 REDIS_REPL_ONLINE
整个复制过程完成,流程如下图所示:



Redis复制机制的缺陷
从上面的流程可以看出,Slave从库在连接Master主库时,Master会进行内存快照,然后把整个快照文件发给Slave,也就是没有象M
ySQL那样有复制位置的概念,即无增量复制,这会给整个集群搭建带来非常多的问题。
比如一台线上正在运行的Master主库配置了一台从库进行简单读写分离,这时Slave由于网络或者其它原因与Master断开了连接,那么
 Slave进行重新连接时,需要重新获取整个Master的内存快照,Slave所有数据跟着全部清除,然后重新建立整个内存表,一方面S
lave恢复的 时间会非常慢,另一方面也会给主库带来压力。
所以基于上述原因,如果你的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,那么根据上一篇我们讨论的Redis磁盘IO的问题,我们大约需要3~4台服
务器来存储。这个实际是对现有 业务情况所做的一个容量规划,假如业务增长很快,很快就会发现当前的容量已经不够了,Redis
面存储的数据很快就会超过物理内存大小,那么如何进行 Redis的在线扩容呢?
Redis的作者提出了一种叫做presharding的方案来解决动态扩容和数据分区的问题,实际就是在同一台机器上部署多个Redis实例的
方式,当容量不够时将多个实例拆分到不同的机器上,这样实际就达到了扩容的效果。
拆分过程如下:
在新机器上启动好对应端口的Redis实例。
配置新端口为待迁移端口的从库。
待复制完成,与主库完成同步后,切换所有客户端配置到新的从库的端口。
配置从库为新的主库。
移除老的端口实例。
重复上述过程迁移好所有的端口到指定服务器上。
以上拆分流程是Redis作者提出的一个平滑迁移的过程,不过该拆分方法还是很依赖Redis本身的复制功能的,如果主库快照数据文件
过大,这个复制的过程也会很久,同时会给主库带来压力。所以做这个拆分的过程最好选择为业务访问低峰时段进行。
Redis复制的改进思路
我们线上的系统使用了我们自己改进版的Redis,主要解决了Redis没有增量复制的缺陷,能够完成类似Mysql Binlog那样可以通过从
库请求日志位置进行增量复制。
我们的持久化方案是首先写RedisAOF文件,并对这个AOF文件按文件大小进行自动分割滚动,同时关闭RedisRewrite命令,然后
 会在业务低峰时间进行内存快照存储,并把当前的AOF文件位置一起写入到快照文件中,这样我们可以使快照文件与AOF文件的位置保持
 一致性,这样我们得到 了系统某一时刻的内存快照,并且同时也能知道这一时刻对应的AOF文件的位置,那么当从库发送同步命令时,

 我们首先会把快照文件发送给从库,然后从库会取 出该快照文件中存储的AOF文件位置,并将该位置发给主库,主库会随后发送该位置
 之后的所有命令,以后的复制就都是这个位置之后的增量信息了。



RedisMySQL的结合
目前大部分互联网公司使用MySQL作为数据的主要持久化存储,那么如何让RedisMySQL很好的结合在一起呢?我们主要使用了一种基
MySQL作为主库,Redis作为高速数据查询从库的异构读写分离的方案。
为此我们专门开发了自己的MySQL复制工具,可以方便的实时同步MySQL中的数据到Redis上。



MySQL-Redis 异构读写分离)
总结:
Redis的复制功能没有增量复制,每次重连都会把主库整个内存快照发给从库,所以需要避免向在线服务的压力较大的主库上增加从库。
Redis 的复制由于会使用快照持久化方式,所以如果你的Redis持久化方式选择的是日志追加方式(aof),那么系统有可能在同一时刻
既做aof日志文件的同步 刷写磁盘,又做快照写磁盘操作,这个时候Redis的响应能力会受到影响。所以如果选用aof持久化,则加从
库需要更加谨慎。
可以使用主动复制和presharding方法进行Redis集群搭建与在线扩容。
本文加上之前的2篇文章基本将Redis的最常用功能和使用场景与优化进行了分析和讨论,实际Redis还有很多其它辅助的一些功能,
Redis的作者也在不断尝试新的思路,这里就不一一列举了,有兴趣的朋友可以研究下