redis集群规范详解
本文档翻译自http://redis.io/topics/cluster-spec。
引言
这个文档是正在开发中的Redis集群功能的规范(specification)文档,文档分为两个部分:
第二部分介绍目前仍未实现的那些功能。
文档各个部分的内容可能会随着集群功能的设计修改而发生改变,其中,未实现功能发生修改的几率比已实现功能发生修改的几率要高。
这个规范包含了编写客户端库(clientlibrary)所需的全部知识,不过请注意,这里列出的一部分细节可能会在未来发生变化。
什么是Redis集群?
Redis集群是一个分布式(distributed)、容错(fault-tolerant)的Redis实现,集群可以使用的功能是普通单机Redis所能使用的功能的一个子集(subset)。
Redis集群中不存在中心(central)节点或者代理(proxy)节点,集群的其中一个主要设计目标是达到线性可扩展性(linearscalability)。
Redis集群为了保证一致性(consistency)而牺牲了一部分容错性:系统会在保证对网络断线(netsplit)和节点失效(nodefailure)具有有限(limited)抵抗力的前提下,尽可能地保持数据的一致性。
集群将节点失效视为网络断线的其中一种特殊情况。
集群的容错功能是通过使用主节点(master)和从节点(slave)两种角色(role)的节点(node)来实现的:
不过,如果不需要保证“先写入,后读取”操作的一致性(read-after-writeconsistency),那么可以使用从节点来执行只读查询。
Redis集群实现的功能子集
Redis集群实现了单机Redis中,所有处理单个数据库键的命令。
针对多个数据库键的复杂计算操作,比如集合的并集操作、合集操作没有被实现,那些理论上需要使用多个节点的多个数据库键才能完成的命令也没有被实现。
在将来,用户也许可以通过MIGRATECOPY命令,在集群的计算节点(computationnode)中执行针对多个数据库键的只读操作,但集群本身不会去实现那些需要将多个数据库键在多个节点中移来移去的复杂多键命令。
Redis集群不像单机Redis那样支持多数据库功能,集群只使用默认的0号数据库,并且不能使用SELECT命令。
Redis集群协议中的客户端和服务器
Redis集群中的节点有以下责任:
记录集群的状态,包括键到正确节点的映射(mappingkeystorightnodes)。
自动发现其他节点,识别工作不正常的节点,并在有需要时,在从节点中选举出新的主节点。
为了执行以上列出的任务,集群中的每个节点都与其他节点建立起了“集群连接(clusterbus)”,该连接是一个TCP连接,使用二进制协议进行通讯。
节点之间使用Gossip协议来进行以下工作:
向其他节点发送PING数据包,以此来检查目标节点是否正常运作。
在特定事件发生时,发送集群信息。
除此之外,集群连接还用于在集群中发布或订阅信息。
因为集群节点不能代理(proxy)命令请求,所以客户端应该在节点返回-MOVED或者-ASK转向(redirection)错误时,自行将命令请求转发至其他节点。
因为客户端可以自由地向集群中的任何一个节点发送命令请求,并可以在有需要时,根据转向错误所提供的信息,将命令转发至正确的节点,所以在理论上来说,客户端是无须保存集群状态信息的。
不过,如果客户端可以将键和节点之间的映射信息保存起来,可以有效地减少可能出现的转向次数,籍此提升命令执行的效率。
键分布模型
Redis集群的键空间被分割为16384个槽(slot),集群的最大节点数量也是16384个。
推荐的最大节点数量为1000个左右。
每个主节点都负责处理16384个哈希槽的其中一部分。
当我们说一个集群处于“稳定”(stable)状态时,指的是集群没有在执行重配置(reconfiguration)操作,每个哈希槽都只由一个节点进行处理。
重配置指的是将某个/某些槽从一个节点移动到另一个节点。
一个主节点可以有任意多个从节点,这些从节点用于在主节点发生网络断线或者节点失效时,对主节点进行替换。
以下是负责将键映射到槽的算法:
HASH_SLOT=CRC16(key)mod16384
以下是该算法所使用的参数:
结果的长度:16位
多项数(poly):1021(也即是x16+x12+x5+1)
初始化值:0000
反射输入字节(ReflectInputbyte):False
发射输出CRC(ReflectOutputCRC):False
用于CRC输出值的异或常量(XorconstanttooutputCRC):0000
该算法对于输入"123456789"的输出:31C3
附录A中给出了集群所使用的CRC16算法的实现。
CRC16算法所产生的16位输出中的14位会被用到。
在我们的测试中,CRC16算法可以很好地将各种不同类型的键平稳地分布到16384个槽里面。
集群节点属性
每个节点在集群中都有一个独一无二的ID,该ID是一个十六进制表示的160位随机数,在节点第一次启动时由/dev/urandom生成。
节点会将它的ID保存到配置文件,只要这个配置文件不被删除,节点就会一直沿用这个ID。
节点ID用于标识集群中的每个节点。一个节点可以改变它的IP和端口号,而不改变节点ID。集群可以自动识别出IP/端口号的变化,并将这一信息通过Gossip协议广播给其他节点知道。
以下是每个节点都有的关联信息,并且节点会将这些信息发送给其他节点:
节点的标志(flags)。
节点负责处理的哈希槽。
节点最近一次使用集群连接发送PING数据包(packet)的时间。
节点最近一次在回复中接收到PONG数据包的时间。
集群将该节点标记为下线的时间。
该节点的从节点数量。
如果该节点是从节点的话,那么它会记录主节点的节点ID。如果这是一个主节点的话,那么主节点ID这一栏的值为0000000。
以上信息的其中一部分可以通过向集群中的任意节点(主节点或者从节点都可以)发送CLUSTERNODES命令来获得。
以下是一个向集群中的主节点发送CLUSTERNODES命令的例子,该集群由三个节点组成:
$redis-cliclusternodes d1861060fe6a534d42d8a19aeb36600e18785e04:0myself-01318428930connected0-1364 3886e65cc906bfd9b1f7e7bde468726a052d1dae127.0.0.1:6380master-13184289301318428931connected1365-2729 d289c575dcbc4bdd2931585fd4339089e461a27d127.0.0.1:6381master-13184289311318428931connected2730-4095
在上面列出的三行信息中,从左到右的各个域分别是:节点ID,IP地址和端口号,标志(flag),最后发送PING的时间,最后接收PONG的时间,连接状态,节点负责处理的槽。
节点握手(已实现)
节点总是应答(accept)来自集群连接端口的连接请求,并对接收到的PING数据包进行回复,即使这个PING数据包来自不可信的节点。
然而,除了PING之外,节点会拒绝其他所有并非来自集群节点的数据包。
要让一个节点承认另一个节点同属于一个集群,只有以下两种方法:
另外,如果一个可信节点向另一个节点传播第三者节点的信息,那么接收信息的那个节点也会将第三者节点识别为集群中的一份子。也即是说,如果A认识B,B认识C,并且B向A传播关于C的信息,那么A也会将C识别为集群中的一份子,并尝试连接C。
这意味着如果我们将一个/一些新节点添加到一个集群中,那么这个/这些新节点最终会和集群中已有的其他所有节点连接起来。
这说明只要管理员使用CLUSTERMEET命令显式地指定了可信关系,集群就可以自动发现其他节点。
这种节点识别机制通过防止不同的Redis集群因为IP地址变更或者其他网络事件的发生而产生意料之外的联合(mix),从而使得集群更具健壮性。
当节点的网络连接断开时,它会主动连接其他已知的节点。
MOVED转向
一个Redis客户端可以向集群中的任意节点(包括从节点)发送命令请求。节点会对命令请求进行分析,如果该命令是集群可以执行的命令,那么节点会查找这个命令所要处理的键所在的槽。
如果要查找的哈希槽正好就由接收到命令的节点负责处理,那么节点就直接执行这个命令。
另一方面,如果所查找的槽不是由该节点处理的话,节点将查看自身内部所保存的哈希槽到节点ID的映射记录,并向客户
端回复一个MOVED错误。
以下是一个MOVED错误的例子:
GETx -MOVED3999127.0.0.1:6381
错误信息包含键x所属的哈希槽3999,以及负责处理这个槽的节点的IP和端口号127.0.0.1:6381。客户端需要根据这个IP和端口号,向所属的节点重新发送一次GET命令请求。
注意,即使客户端在重新发送GET命令之前,等待了非常久的时间,以至于集群又再次更改了配置,使得节点127.0.0.1:6381已经不再处理槽3999,那么当客户端向节点127.0.0.1:6381发送GET命令的时候,节点将再次向客户端返回MOVED错误,指示现在负责处理槽3999的节点。
虽然我们用ID来标识集群中的节点,但是为了让客户端的转向操作尽可能地简单,节点在MOVED错误中直接返回目标节点的IP和端口号,而不是目标节点的ID。
虽然不是必须的,但一个客户端应该记录(memorize)下“槽3999由节点127.0.0.1:6381负责处理“这一信息,这样当再次有命令需要对槽3999执行时,客户端就可以加快寻找正确节点的速度。
注意,当集群处于稳定状态时,所有客户端最终都会保存有一个哈希槽至节点的映射记录(mapofhashslotstonodes),使得集群非常高效:客户端可以直接向正确的节点发送命令请求,无须转向、代理或者其他任何可能发生单点故障(singlepointfailure)的实体(entiy)。
除了MOVED转向错误之外,一个客户端还应该可以处理稍后介绍的ASK转向错误。
集群在线重配置(livereconfiguration)
Redis集群支持在集群运行的过程中添加或者移除节点。
实际上,节点的添加操作和节点的删除操作可以抽象成同一个操作,那就是,将哈希槽从一个节点移动到另一个节点:
添加一个新节点到集群,等于将其他已存在节点的槽移动到一个空白的新节点里面。
从集群中移除一个节点,等于将被移除节点的所有槽移动到集群的其他节点上面去。
因此,实现Redis集群在线重配置的核心就是将槽从一个节点移动到另一个节点的能力。因为一个哈希槽实际上就是一些键的集合,所以Redis集群在重哈希(rehash)时真正要做的,就是将一些键从一个节点移动到另一个节点。
要理解Redis集群如何将槽从一个节点移动到另一个节点,我们需要对CLUSTER命令的各个子命令进行介绍,这些命理负责管理集群节点的槽转换表(slotstranslationtable)。
以下是CLUSTER命令可用的子命令:
CLUSTERADDSLOTSslot1[slot2]...[slotN] CLUSTERDELSLOTSslot1[slot2]...[slotN] CLUSTERSETSLOTslotNODEnode CLUSTERSETSLOTslotMIGRATINGnode CLUSTERSETSLOTslotIMPORTINGnode
最开头的两条命令ADDSLOTS和DELSLOTS分别用于向节点指派(assign)或者移除节点,当槽被指派或者移除之后,节点会将这一信息通过Gossip协议传播到整个集群。ADDSLOTS命令通常在新创建集群时,作为一种快速地将各个槽指派给各个节点的手段来使用。
CLUSTERSETSLOTslotNODEnode子命令可以将指定的槽slot指派给节点node。
至于CLUSTERSETSLOTslotMIGRATINGnode命令和CLUSTERSETSLOTslotIMPORTINGnode命令,前者用于将给定节点node中的槽slot迁移出节点,而后者用于将给定槽slot导入到节点node:
当一个槽被设置为MIGRATING状态时,原来持有这个槽的节点仍然会继续接受关于这个槽的命令请求,但只有命令所处理的键仍然存在于节点时,节点才会处理这个命令请求。
如果命令所使用的键不存在与该节点,那么节点将向客户端返回一个-ASK转向(redirection)错误,告知客户端,要将命令请求发送到槽的迁移目标节点。
当一个槽被设置为IMPORTING状态时,节点仅在接收到ASKING命令之后,才会接受关于这个槽的命令请求。
如果客户端没有向节点发送ASKING命令,那么节点会使用-MOVED转向错误将命令请求转向至真正负责处理这个槽的节点。
上面关于MIGRATING和IMPORTING的说明有些难懂,让我们用一个实际的实例来说明一下。
假设现在,我们有A和B两个节点,并且我们想将槽8从节点A移动到节点B,于是我们:
向节点B发送命令CLUSTERSETSLOT8IMPORTINGA
向节点A发送命令CLUSTERSETSLOT8MIGRATINGB
每当客户端向其他节点发送关于哈希槽8的命令请求时,这些节点都会向客户端返回指向节点A的转向信息:
如果命令要处理的键已经存在于槽8里面,那么这个命令将由节点A处理。
如果命令要处理的键未存在于槽8里面(比如说,要向槽添加一个新的键),那么这个命令由节点B处理。
这种机制将使得节点A不再创建关于槽8的任何新键。
与此同时,一个特殊的客户端redis-trib以及Redis集群配置程序(configurationutility)会将节点A中槽8里面的键移动到节点B。
键的移动操作由以下两个命令执行:
CLUSTERGETKEYSINSLOTslotcount
上面的命令会让节点返回count个slot槽中的键,对于命令所返回的每个键,redis-trib都会向节点A发送一条MIGRATE命令,该命令会将所指定的键原子地(atomic)从节点A移动到节点B(在移动键期间,两个节点都会处于阻塞状态,以免出现竞争条件)。
以下为MIGRATE命令的运作原理:
MIGRATEtarget_hosttarget_portkeytarget_databaseidtimeout
执行MIGRATE命令的节点会连接到target节点,并将序列化后的key数据发送给target,一旦target返回OK,节点就将自己的key从数据库中删除。
从一个外部客户端的视角来看,在某个时间点上,键key要么存在于节点A,要么存在于节点B,但不会同时存在于节点A和节点B。
因为Redis集群只使用0号数据库,所以当MIGRATE命令被用于执行集群操作时,target_database的值总是0。
target_database参数的存在是为了让MIGRATE命令成为一个通用命令,从而可以作用于集群以外的其他功能。
我们对MIGRATE命令做了优化,使得它即使在传输包含多个元素的列表键这样的复杂数据时,也可以保持高效。
不过,尽管MIGRATE非常高效,对一个键非常多、并且键的数据量非常大的集群来说,集群重配置还是会占用大量的时间,可能会导致集群没办法适应那些对于响应时间有严格要求的应用程序。
ASK转向
在之前介绍MOVED转向的时候,我们说除了MOVED转向之外,还有另一种ASK转向。
当节点需要让一个客户端长期地(permanently)将针对某个槽的命令请求发送至另一个节点时,节点向客户端返回MOVED转向。
另一方面,当节点需要让客户端仅仅在下一个命令请求中转向至另一个节点时,节点向客户端返回ASK转向。
比如说,在我们上一节列举的槽8的例子中,因为槽8所包含的各个键分散在节点A和节点B中,所以当客户端在节点A中没找到某个键时,它应该转向到节点B中去寻找,但是这种转向应该仅仅影响一次命令查询,而不是让客户端每次都直接去查找节点B:在节点A所持有的属于槽8的键没有全部被迁移到节点B之前,客户端应该先访问节点A,然后再访问节点B。
因为这种转向只针对16384个槽中的其中一个槽,所以转向对集群造成的性能损耗属于可接受的范围。
因为上述原因,如果我们要在查找节点A之后,继续查找节点B,那么客户端在向节点B发送命令请求之前,应该先发送一个ASKING命令,否则这个针对带有IMPORTING状态的槽的命令请求将被节点B拒绝执行。
接收到客户端ASKING命令的节点将为客户端设置一个一次性的标志(flag),使得客户端可以执行一次针对IMPORTING状态的槽的命令请求。
从客户端的角度来看,ASK转向的完整语义(semantics)如下:
先发送一个ASKING命令,然后再发送真正的命令请求。
不必更新客户端所记录的槽8至节点的映射:槽8应该仍然映射到节点A,而不是节点B。
一旦节点A针对槽8的迁移工作完成,节点A在再次收到针对槽8的命令请求时,就会向客户端返回MOVED转向,将关于槽8的命令请求长期地转向到节点B。
注意,即使客户端出现Bug,过早地将槽8映射到了节点B上面,但只要这个客户端不发送ASKING命令,客户端发送命令请求的时候就会遇上MOVED错误,并将它转向回节点A。
容错
节点失效检测
以下是节点失效检查的实现方法:
等待PING命令回复的时限称为“节点超时时限(nodetimeout)”,是一个节点选项(node-wisesetting)。
每次当节点对其他节点发送PING命令的时候,它都会随机地广播三个它所知道的节点的信息,这些信息里面的其中一项就是说明节点是否已经被标记为PFAIL或者FAIL。
当节点接收到其他节点发来的信息时,它会记下那些被其他节点标记为失效的节点。这称为失效报告(failurereport)。
如果节点已经将某个节点标记为PFAIL,并且根据节点所收到的失效报告显式,集群中的大部分其他主节点也认为那个节点进入了失效状态,那么节点会将那个失效节点的状态标记为FAIL。
一旦某个节点被标记为FAIL,关于这个节点已失效的信息就会被广播到整个集群,所有接收到这条信息的节点都会将失效节点标记为FAIL。
简单来说,一个节点要将另一个节点标记为失效,必须先询问其他节点的意见,并且得到大部分主节点的同意才行。
因为过期的失效报告会被移除,所以主节点要将某个节点标记为FAIL的话,必须以最近接收到的失效报告作为根据。
在以下两种情况中,节点的FAIL状态会被移除:
保持(retaning)从节点的FAIL状态是没有意义的,因为它不处理任何槽,一个从节点是否处于FAIL状态,决定了这个从节点在有需要时能否被提升为主节点。
如果一个主节点被打上FAIL标记之后,经过了节点超时时限的四倍时间,再加上十秒钟之后,针对这个主节点的槽的故障转移操作仍未完成,并且这个主节点已经重新上线的话,那么移除对这个节点的FAIL标记。
在第二种情况中,如果故障转移未能顺利完成,并且主节点重新上线,那么集群就继续使用原来的主节点,从而免去管理员介入的必要。
集群状态检测(已部分实现)
每当集群发生配置变化时(可能是哈希槽更新,也可能是某个节点进入失效状态),集群中的每个节点都会对它所知道的节点进行扫描(scan)。
一旦配置处理完毕,集群会进入以下两种状态的其中一种:
OK:集群可以正常工作,负责处理全部16384个槽的节点中,没有一个节点被标记为FAIL状态。
这说明即使集群中只有一部分哈希槽不能正常使用,整个集群也会停止处理任何命令。
不过节点从出现问题到被标记为FAIL状态的这段时间里,集群仍然会正常运作,所以集群在某些时候,仍然有可能只能处理针对16384个槽的其中一个子集的命令请求。
以下是集群进入FAIL状态的两种情况:
集群中的大部分主节点都进入下线状态。当大部分主节点都进入PFAIL状态时,集群也会进入FAIL状态。
第二个检查是必须的,因为要将一个节点从PFAIL状态改变为FAIL状态,必须要有大部分主节点进行投票表决,但是,当集群中的大部分主节点都进入失效状态时,单凭一个两个节点是没有办法将一个节点标记为FAIL状态的。
因此,有了第二个检查条件,只要集群中的大部分主节点进入了下线状态,那么集群就可以在不请求这些主节点的意见下,将某个节点判断为FAIL状态,从而让整个集群停止处理命令请求。
从节点选举
一旦某个主节点进入FAIL状态,如果这个主节点有一个或多个从节点存在,那么其中一个从节点会被升级为新的主节点,而其他从节点则会开始对这个新的主节点进行复制。
新的主节点由已下线主节点属下的所有从节点中自行选举产生,以下是选举的条件:
已下线主节点负责处理的槽数量非空。
从节点的数据被认为是可靠的,也即是,主从节点之间的复制连接(replicationlink)的断线时长不能超过节点超时时限(nodetimeout)乘以REDIS_CLUSTER_SLAVE_VALIDITY_MULT常量得出的积。
如果一个从节点满足了以上的所有条件,那么这个从节点将向集群中的其他主节点发送授权请求,询问它们,是否允许自己(从节点)升级为新的主节点。
如果发送授权请求的从节点满足以下属性,那么主节点将向从节点返回FAILOVER_AUTH_GRANTED授权,同意从节点的
升级要求:
一旦某个从节点在给定的时限内得到大部分主节点的授权,它就会开始执行以下故障转移操作:
所有其他节点都会根据新的主节点对配置进行相应的更新,特别地:
在集群的生命周期中,如果一个带有PROMOTED标识的主节点因为某些原因转变成了从节点,那么该节点将丢失它所带有的PROMOTED标识。
发布/订阅(已实现,但仍然需要改善)
在一个Redis集群中,客户端可以订阅任意一个节点,也可以向任意一个节点发送信息,节点会对客户端所发送的信息进行转发。
在目前的实现中,节点会将接收到的信息广播至集群中的其他所有节点,在将来的实现中,可能会使用bloomfilter或者其他算法来优化这一操作。
附录A:CRC16算法的ANSI实现参考
/* *Copyright2001-2010GeorgesMenie(www.menie.org) *Copyright2010SalvatoreSanfilippo(adaptedtoRediscodingstyle) *Allrightsreserved. *Redistributionanduseinsourceandbinaryforms,withorwithout *modification,arepermittedprovidedthatthefollowingconditionsaremet: * **Redistributionsofsourcecodemustretaintheabovecopyright *notice,thislistofconditionsandthefollowingdisclaimer. **Redistributionsinbinaryformmustreproducetheabovecopyright *notice,thislistofconditionsandthefollowingdisclaimerinthe *documentationand/orothermaterialsprovidedwiththedistribution. **NeitherthenameoftheUniversityofCalifornia,Berkeleynorthe *namesofitscontributorsmaybeusedtoendorseorpromoteproducts *derivedfromthissoftwarewithoutspecificpriorwrittenpermission. * *THISSOFTWAREISPROVIDEDBYTHEREGENTSANDCONTRIBUTORS``ASIS''ANDANY *EXPRESSORIMPLIEDWARRANTIES,INCLUDING,BUTNOTLIMITEDTO,THEIMPLIED *WARRANTIESOFMERCHANTABILITYANDFITNESSFORAPARTICULARPURPOSEARE *DISCLAIMED.INNOEVENTSHALLTHEREGENTSANDCONTRIBUTORSBELIABLEFORANY *DIRECT,INDIRECT,INCIDENTAL,SPECIAL,EXEMPLARY,ORCONSEQUENTIALDAMAGES *(INCLUDING,BUTNOTLIMITEDTO,PROCUREMENTOFSUBSTITUTEGOODSORSERVICES; *LOSSOFUSE,DATA,ORPROFITS;ORBUSINESSINTERRUPTION)HOWEVERCAUSEDAND *ONANYTHEORYOFLIABILITY,WHETHERINCONTRACT,STRICTLIABILITY,ORTORT *(INCLUDINGNEGLIGENCEOROTHERWISE)ARISINGINANYWAYOUTOFTHEUSEOFTHIS *SOFTWARE,EVENIFADVISEDOFTHEPOSSIBILITYOFSUCHDAMAGE. */ /*CRC16implementationacordingtoCCITTstandards. * *Noteby@antirez:thisisactuallytheXMODEMCRC16algorithm,usingthe *followingparameters: * *Name:"XMODEM",alsoknownas"ZMODEM","CRC-16/ACORN" *Width:16bit *Poly:1021(Thatisactuallyx^16+x^12+x^5+1) *Initialization:0000 *ReflectInputbyte:False *ReflectOutputCRC:False *XorconstanttooutputCRC:0000 *Outputfor"123456789":31C3 */ staticconstuint16_tcrc16tab[256]={ 0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7, 0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef, 0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6, 0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de, 0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485, 0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d, 0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4, 0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc, 0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823, 0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b, 0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12, 0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a, 0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41, 0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49, 0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70, 0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78, 0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f, 0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067, 0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e, 0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256, 0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d, 0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405, 0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c, 0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634, 0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab, 0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3, 0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a, 0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92, 0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9, 0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1, 0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8, 0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0 }; uint16_tcrc16(constchar*buf,intlen){ intcounter; uint16_tcrc=0; for(counter=0;counter>8)^*buf++)&0x00FF]; returncrc; }
总结
以上就是本文关于redis集群规范详解的全部内容,希望对大家有所帮助。感兴趣的朋友可以参阅:详细分析Redis集群故障、Redis源码解析:集群手动故障转移、从节点迁移详解、简述Redis和MySQL的区别等,如有不足之处,请留言指出,小编悉心更正。感谢朋友们对毛票票网站的支持!