写在前面
配置集群
1、配置集群,集群解决了单点故障以及单台机器内存上限的问题,使用集群时,只需要将配置文件中的参数cluster-enabled打开即可,集群中至少有三个主库才可以运行,当启动若干个redis-server后,此时每个节点都是独立的,想要初始化集群,redis源码中提供了一个使用ruby语言编写的工具,redis-trib.rb来辅助初始化集群。
2、使用redis-trib.rb初始化集群,只需要执行:
redis-trib.rb create --replicas n [ip:port ...]
create 表示初始化集群,--replicas n表示每个主库拥有的从库数量,执行命令后,会输出一系列信息,包含节点的启动信息(是否启动成功),以及主从信息,如果觉得没问题,输入yes来开始创建。
3、redis-trib.rb会像客户端一样尝试PING每个节点,如果PING失败则集群启动失败,之后会发送INFO命令获取每个节点的runid以及是否开启了集群;以上就绪后,会向每一个节点发送CLUSTER MEET ip port命令,用来告诉当前节点指定ip和port上运行的节点也是集群的一部分;接着开始分配主从数据库,redis-trib.rb会尽量使得主从库不在同意IP上,以保证容灾能力;然后为每一个主库分配插槽(用来分配哪些键由哪个库存储),最后向每个要成为从库的节点发送CLUSTER REPLICATE 主库runid命令,来将该节点转换成从库并复制指定运行id的节点,至此集群初始化完毕。
4、集群初始化完毕后可以使用redis-cli连接任意节点即可获取整个集群的信息(使用CLUSTER NODES命令),也可以尝试使用redis-cli初始化一次集群。
增加节点
5、向集群中增加节点
CLUSTER MEET ip port
增加节点时,只需要使用客户端向新增节点发送该命令;ip 和 port 分别是集群中已有的某个节点的ip和port。
6、新增节点你会向集群中的节点进行握手,握手成功后新节点将被认作集群的一员,被握手的节点会使用Gossip协议通知集群中的其他节点关于新增节点的信息。
插槽
7、redis使用CRC16算法将每个键的有效部分计算出散列值对16384取余,使得每个键都可以分配至16384个插槽中;键名的有效部分有以下规则:
a、当键名包含{符号,且在其后存在}符号,并且两者之间至少有一个字符,那有效部分指的是{和}之间的内容。
b、如果不满足上一条,整个键名都是有效部分。
8、在使用工具redis-trib.rb初始化集群时,每个节点会被分配到连续的插槽,但redis并没有这个限制,可以将任意几个插槽交给特定的节点负责。
9、查看插槽的分配情况
CLUSTER SLOTS
返回信息的数量会是多条,每条信息中包含槽的开始和结束号码以及主从信息。
10、分配未被分配的插槽
CLUSTER ADDSLOTS slots [slots ...]
若分配了已经分配出去的插槽,则会返回错误。
11、若要移动插槽给另外的节点,也可以使用redis-trib.rb工具:
redis-trib.rb reshared ip port
reshared告诉redis-trib.rb要重新分片,ip和port是集群中任意节点的地址和端口;之后redis-trib.rb会询问要迁移多少个插槽,要把插槽迁移到哪个节点(通过运行id确定节点)要从哪个节点移出插槽,最后输入done即可。
12、若不借助redis-trib.rb移动插槽,可以使用如下命令:
CLUSTER SETSLOT 插槽号 NODE 新节点的运行id
但使用该命令的前提是,插槽中没有键,因为该命令只会迁移插槽,不会迁移键,使用如下命令获取插槽中的键:
CLUSTER GETKEYSINSLOT 插槽号 要返回的键的数量
之后对每个键进行迁移,命令如下:
MIGRATE 目标节点地址 目标节点端口 键名 数据库号码 超时时间 [copy] [replace]
copy选项表示不从当前库中删除,而是复制一份副本,replace表示如果存在相同键名则覆盖,数据库号码始终是0。
13、以上的方法仍然会造成数据临时丢失,redis提供了如下两个命令来实现集群不下线的情况下迁移数据:
CLUSTER SETSLOT 插槽号 MIGRATING 新节点runid
CLUSTER SETSLOT 插槽号 IMPORTING 原节点runid
redis-trib.rb工具在迁移时就会先执行上面两条命令,用来解决临时丢失问题,之后获取插槽中存储的键,一一迁移,最后迁移插槽。
14、执行上面的前两个命令后,当客户端向原节点要迁移的插槽请求一个键时,若该键未被迁移,则直接返回,若已迁移,返回ASK跳转命令告诉客户端新的节点,客户端会先向新节点发送ASKING命令,之后重新执行最初的请求键的命令;相反,如果客户端向新节点请求了一个正在迁移的插槽中的键,如果前面执行过ASKING命令则直接返回,如果没有执行过则返回MOVE跳转命令,重新告诉客户端到原节点去请求键。
获取与插槽对应的节点
15、对于指定的键,会根据CRC16算法分配到对应的插槽,那么如何获知插槽所在的节点,当客户端请求一个不在当前节点负责的插槽中的键时,redis会返回MOVE重定向指令:
MOVE slot ip port
包含对应键所在的插槽号和该插槽所在的节点的地址和端口,客户端收到MOVE指令后,再到对应节点请求键。
16、一些语言的redis库支持MOVE请求,此时对开发者而言这是透明的,使用-c参数启动redis-cli则会以集群模式启动,也支持自动重定向;为解决由于重定向导致的多次的网络请求,客户端应缓存插槽的路由信息,以达到与单机同样的性能。
故障恢复
17、集群中每个几点会每秒挑选另外五个节点,对其中最久没有相应的节点发送PING,如果一定时间没有回复,发起PING的节点就会认为其疑似下线,与哨兵的主观下线类似。
18、一旦节点A认为B疑似下线,就会在集群中传递这个消息,当某一节点收到半数以上认为B下线的消息,就会向集群中传播B已下线的消息。
19、当集群中有主库下线,则会导致一部分插槽无法写入,这时如果主库拥有至少一个从库,集群就会进行故障恢复将一个从库转变为主库;选择哪个主库,与选择领头哨兵的过程一致,都基于RAFT算法。
20、如果至少负责一个插槽且没有从库的主库下线,集群默认进入下线状态无法工作,可通过修改配置参数cluster-require-full-coverage为yes来使其在这种情况仍可以工作。
集群的一些限制
21、当涉及多键命令时,如果想关键不在同一个节点,则会执行出错,可以利用键名的有效部份这一特点,将要操作的多键的键名使用{}改造,保证其在同一节点。
22、集群中的每个节只能使用0号数据库。