集群与主从的区别
主从模式中,客户端可以从任何一个服务端读取,分散了读的压力,但是只能对特定的一个服务端做写操作。redis 提供了 sentinel 模式监控主服务的状态,如果主服务挂了,会选择一台从服务作为主服务。可是如果住服务是因为写压力过大,那么相同配置的从服务被选为主之后,毫无疑问也会因为压力太大而挂掉。而且 redis 所有数据都是保存在内存里,如果数据太多,一台服务器放满了,也不能用主从模式。这个时候可以用集群方案来解决。
redis 集群方案里,系统由多个主从服务组成,每个服务端都可以读写,服务端会根据哈希算法,分配数据到特定的服务器上。系统内不再有单点压力。
过一遍官方例子
在官网下载源码后编译
1 | $ gem install redis # 安装 ruby 依赖 |
这里确保 src/redis-server, src/redis-cli 两个生成成功。然后进入 utils/create-cluster,执行
1 | $ ./create-cluster start #生成6个实例 |
用客户端测试集群
官方例子生成了6个实例,分别使用 30001-30006 6个端口。
开一个客户端,连接第一个端口
1 | $ redis-cli -c -p 30001 |
开另一个客户端,连接第二个端口
1 | $ redis-cli -c -p 30002 |
这里发现客户端已经被转到了30001这个端口。之后用客户端分别连接每个端口,执行 get 和 set 操作,发现 在任何一个客户端都是可以执行set 和 get 操作的,系统会判断值应该被存储在哪个服务,然后转接过去。
最后清理现场
1 | $ ./create-cluster stop #停止所有实例 |
手动生成集群
生成实例
先来分析一下 create-cluster 的代码。当执行 start 的时候,实际上是执行了
1 | redis-server --port $PORT --cluster-enabled yes --cluster-config-file nodes-${PORT}.conf --cluster-node-timeout 2000 --appendonly yes --appendfilename appendonly-${PORT}.aof --dbfilename dump-${PORT}.rdb --logfile ${PORT}.log --daemonize yes |
$PORT 是30001-30006。如果目录下没有 nodes-${PORT}.conf 配置文件,redis 会自己生成一个,并写入信息。现在先手动生成实例 30001-30004。
1 | $ ps aux | grep redis |
连接各节点
只要连接并操作一个一个服务端即可
1 | 127.0.0.1:30001> CLUSTER MEET 127.0.0.1 30002 |
查看集群状态
查看集群状态
1 | 127.0.0.1:30001> cluster info |
查看集群内所有节点
1 | 127.0.0.1:30001> cluster nodes |
分配哈希槽
redis集群有16384个哈希槽,要把所有数据映射到16384槽,需要批量设置槽
1 | redis-cli -p 30001 cluster addslots {0..8192} |
做主从映射
我的配置是 30001主->30002从,30003主->30004从
1 | 127.0.0.1:30002> CLUSTER REPLICATE 69a812b0dfc02dee94bd03a8e1ff453728409e5a |
总结
到此 redis 集群已经搭建好了。现在执行 cluster info,cluster_state 已经是 ok 了。
看得出还是挺麻烦的,推荐使用官方的脚本 redis-trib.rb。当执行 create-cluster create 的时候,是执行了
1 | ../../src/redis-trib.rb create --replicas 1 127.0.0.1:30001 127.0.0.1:3002 127.0.0.1:3003 127.0.0.1:3004 127.0.0.1:3005 127.0.0.1:3006 |
–replicas 1 表示每个主对应一个从。
集群更改
转移插槽(slot)
1 | src/redis-trib.rb reshard 127.0.0.1:30001 |
以上插槽就搬运好了。如果要把一个节点里的所有插槽转移到另一个节点,可以简单的执行这个命令
1 | ./redis-trib.rb reshard --from <node-id> --to <node-id> --slots <number of slots> --yes <host>:<port> |
集群扩容
首先启动两个新的服务 30005主,30006从。
将30005加入集群
1 | src/redis-trib.rb add-node 127.0.0.1:30005 127.0.0.1:30001 #引入主节点 |
其中 03ccad2ba5dd1e062464bc7590400441fafb63f2 是30005的 cluster node id。然后将其他节点的插槽转移到这个节点就好了。
集群缩减
将主节点的插槽全部转移到别的主节点,然后执行
1 | src/redis-trib.rb del-node 127.0.0.1:30005 03ccad2ba5dd1e062464bc7590400441fafb63f2 |
其中 03ccad2ba5dd1e062464bc7590400441fafb63f2 是节点 cluster node id
集群模式的缺陷
- 键的批量操作支持有限,比如mset, mget,如果多个键映射在不同的槽,就不支持了
- 键事务支持有限,当多个key分布在不同节点时无法使用事务,同一节点是支持事务
- 键是数据分区的最小粒度,不能将一个很大的键值对映射到不同的节点
- 不支持多数据库,只有0,select 0
- 复制结构只支持单层结构,不支持树型结构。
- 集群不能少于3个主节点,否则主从切换会失败
参考资料:
官方教程 https://redis.io/topics/cluster-tutorial
redis集群高可用 https://www.cnblogs.com/leeSmall/p/8414687.html