主从模式

最简单的模式。

master啥都不用配置。

slave如下配置。

replicaof [master ip] 6379
replica-serve-stale-data no

启动后就可以形成主从集群。一写多读模式,适用于少写多读的应用,同时对一致性没有强要求,例如缓存。

docker验证

我用如下命令开docker容器做的测试。

export TAG=7-alpine
export FLAGS=-d --restart=always
docker run ${FLAGS} --name master -p 6379:6379 -v $PWD/master/:/data/ redis:${TAG} redis-server redis.conf
docker run ${FLAGS} --name slave -p 6378:6379 -v $PWD/slave/:/data/ redis:${TAG} redis-server redis.conf

当前目录下分别开master和slave目录,里面放redis.conf,启动容器即可形成集群。

用如下命令验证:

docker run -it --rm redis:7-alpine redis-cli -h [master ip] -p 6379 info replication
docker run -it --rm redis:7-alpine redis-cli -h [master ip] -p 6378 info replication
docker run -it --rm redis:7-alpine redis-cli -h [master ip] -p 6379 set abc def
docker run -it --rm redis:7-alpine redis-cli -h [master ip] -p 6378 get abc

sentinel模式

复杂且不好用。

sentinel需要N个redis和M个sentinel。其中M应为奇数,防止脑裂。sentinel会连接每个redis,验证他们的可用性。在master redis下线的时候,M个哨兵通过私下串联,重新选举主,并且将所有redis配置修改到正确的状态。因而,在M个哨兵的监督下,N个redis可以形成自选举的一写多读集群。

先配置N个redis主从,方法同上。理论上N个无关redis也能形成一写多读集群。实际上N个无关redis不会被sentinel自动认为同一个集群。

随后配置三个sentinel。

sentinel monitor redis1 [redis1 ip] 6379 1
sentinel down-after-milliseconds redis1 5000
sentinel parallel-syncs redis1 2
sentinel failover-timeout redis1 300000

sentinel monitor redis2 [redis2 ip] 6379 1
sentinel down-after-milliseconds redis2 5000
sentinel parallel-syncs redis2 2
sentinel failover-timeout redis2 300000

sentinel monitor redis3 [redis3] 6379 1
sentinel down-after-milliseconds redis3 5000
sentinel parallel-syncs redis3 2
sentinel failover-timeout redis3 300000

启动后即可形成sentinel集群。

注意sentinel模式是failover。一旦master下线,再上线会被自动配置成slave。

docker验证

reference2里写明了,sentinel模式不支持NAT内部,因此要用–net=host来配置所有容器。

docker run ${FLAGS} --name redis1 --net=host -v $PWD/redis1/:/data/ redis:${TAG} redis-server redis.conf
docker run ${FLAGS} --name redis2 --net=host -v $PWD/redis2/:/data/ redis:${TAG} redis-server redis.conf
docker run ${FLAGS} --name redis3 --net=host -v $PWD/redis3/:/data/ redis:${TAG} redis-server redis.conf
sleep 1
docker run ${FLAGS} --name sentinel1 --net=host -v $PWD/sentinel1/:/data/ redis:${TAG} redis-server sentinel.conf --sentinel
docker run ${FLAGS} --name sentinel2 --net=host -v $PWD/sentinel2/:/data/ redis:${TAG} redis-server sentinel.conf --sentinel
docker run ${FLAGS} --name sentinel3 --net=host -v $PWD/sentinel3/:/data/ redis:${TAG} redis-server sentinel.conf --sentinel

三个redis的配置文件分别配置端口为6377,6378,6379(因为使用host network)。三个sentinel的端口分别为26377,26378,26379。验证方法和主从模式同。

cluster模式

cluster模式使用分片+副本。

对任意key,redis的分片算法会计算key的CRC16,然后映射到16384个slots中的一个。最后redis维护一张一致性表,记录这16384个slot分别在哪个redis实例上。当增删节点的时候,只要调整这张一致性表就好了。为了增加可用性,这张表还能带主从。每个slot可以配置一个主多个从。可惜cluster模式的从不能分担读压力,只是一个冷备。

但这里似乎有个撞key漏洞。如果攻击者知道redis中的key分布规律,理论上可以构造许多key,均落在一个slot里。使得分布算法无论怎么调整,压力都精确的打到一台redis上,引起DoS。对于sha来说这个计算困难,对于CRC16来说还是不算太难的。因此redis中的key最好不要用用户可选择数据。

另外cluster模式继承了k-v分治模式的同类缺点。对于批量操作跨node的,cluster模式的实现要么不支持,要么有问题(influx-proxy也有类似问题)。

每个节点如下配置:

port 6377/6378/6379
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 15000

docker验证

用如下命令启动集群。

docker run ${FLAGS} --name cluster1 --net=host -v $PWD/cluster1/:/data/ redis:${TAG} redis-server redis.conf
docker run ${FLAGS} --name cluster2 --net=host -v $PWD/cluster2/:/data/ redis:${TAG} redis-server redis.conf
docker run ${FLAGS} --name cluster3 --net=host -v $PWD/cluster3/:/data/ redis:${TAG} redis-server redis.conf
sleep 1
docker run -it --rm redis:7-alpine redis-cli --cluster create 127.0.0.1:6377 127.0.0.1:6378 127.0.0.1:6379

验证的时候,redis-cli的参数里要加-c

reference

  1. 一文掌握Redis主从复制、哨兵、Cluster三种集群模式
  2. High availability with Redis Sentinel
  3. Scaling with Redis Cluster