1. 编译安装
1.1 下载redis
# cd /tmp/
# wget http://download.redis.io/releases/redis-3.2.1.tar.gz
# tar zxvf redis-3.2.1.tar.gz
# cd redis-3.2.1/
1.2 编译redis
# make
错误:
找不到 <jemalloc/jemalloc.h>
解决:
make distclean
然后重新make;redis的代码包里有自带的jemalloc;
1.3 测试
# yum install tcl
# make test
错误1:
You need tcl 8.5 or newer in order to run the Redis test
解决:
# yum install tcl.x86_64
错误2:
[exception]: Executing test client: NOREPLICAS Not enough good slaves to write..
NOREPLICAS Not enough good slaves to write.
......
Killing still running Redis server 63439
Killing still running Redis server 63486
Killing still running Redis server 63519
Killing still running Redis server 63546
Killing still running Redis server 63574
Killing still running Redis server 63591
I/O error reading reply
......
解决:
vim tests/integration/replication-2.tcl
- after 1000
+ after 10000
错误3:
[err]: Slave should be able to synchronize with the master in tests/integration/replication-psync.tcl
Replication not started.
解决:
遇见过一次,重试make test就ok了。
错误4:
[err]: Test replication partial resync: ok psync (diskless: yes, reconnect: 1) in tests/integration/replication-psync.tcl
解决:
vim tests/integration/replication-psync.tcl
- after 100
+ after 1000
1.4 安装redis
# make install
# cp redis.conf /usr/local/etc/
# cp src/redis-trib.rb /usr/local/bin/
2. standalone模式
2.1 配置redis
# vim etc/redis.conf
daemonize yes
logfile "/var/run/redis/log/redis.log"
pidfile /var/run/redis/pid/redis_6379.pid
dbfilename redis.rdb
dir /var/run/redis/rdb/
2.2 启动redis
# mkdir -p /var/run/redis/log
# mkdir -p /var/run/redis/rdb
# mkdir -p /var/run/redis/pid
# /usr/local/bin/redis-server /usr/local/etc/redis.conf
# ps -ef | grep redis
root 71021 1 0 15:46 ? 00:00:00 /usr/local/bin/redis-server 127.0.0.1:6379
2.3 测试redis
# /usr/local/bin/redis-cli
127.0.0.1:6379> set country china
OK
127.0.0.1:6379> get country
"china"
127.0.0.1:6379> set country america
OK
127.0.0.1:6379> get country
"america"
127.0.0.1:6379> exists country
(integer) 1
127.0.0.1:6379> del country
(integer) 1
127.0.0.1:6379> exists country
(integer) 0
127.0.0.1:6379>exit
2.4 停止redis
# /usr/local/bin/redis-cli shutdown
3. master-slave模式
3.1 配置redis
为了测试master-slave模式,我需要在一个host上启动2个redis实例(有条件的话,当然可以使用多个host,每个host运行一个redis实例)。为此,需要把redis.conf复制多份:
# cp /usr/local/etc/redis.conf /usr/local/etc/redis_6379.conf
# cp /usr/local/etc/redis.conf /usr/local/etc/redis_6389.conf
配置实例6379:
<pre name="code" class="plain"># vim /usr/local/etc/redis_6379.conf
daemonize yes
port 6379
logfile "/var/run/redis/log/redis_6379.log"
pidfile /var/run/redis/pid/redis_6379.pid
dbfilename redis_6379.rdb
dir /var/run/redis/rdb/
min-slaves-to-write 1
min-slaves-max-lag 10
最后两项配置表示: 在10秒内,至少1个slave ping过master;
配置实例6389:
<pre name="code" class="plain"># vim /usr/local/etc/redis_6389.conf
daemonize yes
port 6389
slaveof 127.0.0.1 6379
logfile "/var/run/redis/log/redis_6389.log"
pidfile /var/run/redis/pid/redis_6389.pid
dbfilename redis_6389.rdb
dir /var/run/redis/rdb/
repl-ping-slave-period 10
易见,我将要启动两个redis实例,一个使用端口6379(默认端口),另一个使用6389;并且,前者为master,后者为slave。
repl-ping-slave-period表示slave向master发送PING的频率,单位是秒。
另外,在6389的配置文件中,有如下配置可以修改(一般不需修改):
slave-read-only yes
slave-serve-stale-data yes
第一个表示:slave是只读的;
第二个表示:当slave在同步新数据(从master同步数据)的时候,它使用旧的数据服务client。这使得slave是非阻塞的。
在6379的配置文件中,有如下配置可以修改(一般不需修改):
# repl-backlog-size 1mb
repl-diskless-sync no
repl-diskless-sync-delay 5
对于这几项,故事是这样的:
1. 对于一直保持着连接的slave,可以通过增量同步来达到主从一致。
2. 断开重连的slave可能通过部分同步来达到一致(redis 2.8之后才有这一功能,此版本之前,只能和新的slave一样,通过完整同步来达到一致),机制是:
master在内存中记录一些replication积压量;重连的slave与master就replication offset和master run id进行协商:若master run id没变(即master没有重启),并且slave请求的replication offset在积压量里,就可以从offset开始进行部分同步来达到一致。这两个条件任何一个不满足,就必须进行完整同步了。repl-backlog-size 1mb就是用于配置replication积压量大小的。
3. 对于新的slave或者无法通过部分同步达到一致的重连slave,而必须进行完整同步,即传送一个RDB文件。master有两种方式来传这个RDB文件:
- disk-backed:在磁盘上生成RDB文件,然后传送给slave;
- diskless:不在磁盘上生成RDB文件,而是一边生成RDB数据,一边直接写到socket;
repl-diskless-sync用于配置使用哪种策略。对于前者,在磁盘上生成一次RDB文件,可以服务多个slave;而对于后者,一旦传送开始,新来的slave只能排队(等当前slave同步完成)。所以,master在开始传送之前,可能希望推迟一会儿,希望来更多的slave,这样master就可以并行的把生成的数据传送给他们。参数repl-diskless-sync-delay 就是用于配置推迟的时间的,单位秒。
慢磁盘的master,可能需要考虑使用diskless传送。
3.2 启动master
# /usr/local/bin/redis-server /usr/local/etc/redis_6379.conf
# /usr/local/bin/redis-cli
127.0.0.1:6379> set country Japan
(error) NOREPLICAS Not enough good slaves to write.
127.0.0.1:6379>
可见,由于slave没启动,不满足在10秒内,至少1个slave ping过master的条件,故出错。
3.3 启动slave
# /usr/local/bin/redis-server /usr/local/etc/redis_6389.conf
# /usr/local/bin/redis-cli
127.0.0.1:6379> set country Japan
OK
3.4 停止redis
# /usr/local/bin/redis-cli -p 6389 shutdown
# /usr/local/bin/redis-cli -p 6379 shutdown
4. cluster + master-slave模式
- 自动的将数据集分布到不同节点上;
- 当一部分节点故障时,能够继续服务;
- 异步复制导致数据丢失:1. 用户写入master节点B;2. master节点B向client回复OK;3. master节点B把写入数据复制到它的slave。可见,因为B不等slave确认写入就向client回复OK,若master节点B在2之后故障,就会导致数据丢失。
- 网络分裂导致数据丢失:例如有A,B,C三个master,它们的slave分别是A1,B1,C1;如果发生网络分裂,B和客户端分到一侧。在cluster-node-timeout之内,客户端可以继续向B写入数据;当超过cluster-node-timeout时,分裂的另一侧发生fail over,B1当选为master。客户端向B写的数据就丢失了。
- redis(非cluster模式)本身也可能丢失数据:1. RBD定期快照,导致快照周期内的数据丢失;2. AOF,虽然每一个写操作都记入log,但log是定期sync的,也可能丢失sync周期内的数据。
我将在同一台机器上试验redis集群模式,为此,我需要创建6个redis实例,其中3个master,另外3个是slave。它们的端口是7000-7005。
4.1 配置redis集群:
<pre name="code" class="plain"># cp /usr/local/etc/redis.conf /usr/local/etc/redis_7000.conf
# vim /usr/local/etc/redis_7000.conf
daemonize yes
port 7000
pidfile /var/run/redis/pid/redis_7000.pid
logfile "/var/run/redis/log/redis_7000.log"
dbfilename redis_7000.rdb
dir /var/run/redis/rdb/
min-slaves-to-write 0
cluster-enabled yes
cluster-config-file /var/run/redis/nodes/nodes-7000.conf
cluster-node-timeout 5000
cluster-slave-validity-factor 10
repl-ping-slave-period 10
这里我把min-slave-to-write改为0,为了后文验证fail over之后,仍能够读写(否则,master crash之后,slave取代它成为master,但它没有slave,故不能读写)。
# cp /usr/local/etc/redis_7000.conf /usr/local/etc/redis_7001.conf
# cp /usr/local/etc/redis_7000.conf /usr/local/etc/redis_7002.conf
# cp /usr/local/etc/redis_7000.conf /usr/local/etc/redis_7003.conf
# cp /usr/local/etc/redis_7000.conf /usr/local/etc/redis_7004.conf
# cp /usr/local/etc/redis_7000.conf /usr/local/etc/redis_7005.conf
# sed -i -e 's/7000/7001/' /usr/local/etc/redi