一、Redis基本概念和安装使用 1、综述 Redis是一款内存数据库,所谓内存数据库是指它存储数据的主要介质是内存而非传统意义的磁盘,后者只用于辅助功能。Redis可以当作NoSQL数据库,缓存和消息代理来使用,目前各行业实践中使用Redis最多的场景还是把它当成缓存子系统,例如存储在线用户的登录情况,存储1小时内提交的订单情况等,缓存图片路径或者图片内容等等;其次较多的场景是作为消息代理来使用,例如DUBBO支持使用Redis进行事件订阅和通知。 Redis的发起者是SalvatoreSanfilippo,最初开发它的目的就是为了解决快速存储和查询社交网站上常见的好友关系数据。目前Vmware在资助着Redis项目的开发和维护,Redis最新的版本是3。X(本文写作时为version3。2。5),其中内置支持了很多实用的数据存储结构,例如string,hashes,lists,set,sortedsets等,还提供了很多实用的高性能、高可靠特性,例如集合运算、LRU缓存内容管理、主从同步等等。 在本专题讨论Redis时,会首先花一些较少的篇幅介绍Redis的基本安装和使用,然后将深入Redis所支持的数据结构主要讲解Redis底层设计对这些数据结构的支撑,接下来会介绍Redis的主要配置优化事项,最后介绍Redis的集群搭建方式(基于3。X版本)和实施案例。 2、快速安装和基本配置说明 21、快速安装 Redis官方网站非常好记:https:redis。io,安装也非常简单。请到官网下载最新发布的稳定版本:http:download。redis。ioreleasesredis3。2。5。tar。gz,解压后可直接makemakeinstall。注意,类似Centos6。X的老版本操作系统,在安装时可能会报一些错误,依次解决就行了: make:cc:Commandnotfoundmake:makeinstallmake:cc:Commandnotfoundmake 遇到这个错误,说明没有安装编译工具。yum安装gcc即可:yuminstallygcc Youneedtcl8。5ornewerinordermaketesterror:Youneedtcl8。5ornewerinorder 这是因为相应软件的版本太老了,下载一个安装就行:wgethttp:downloads。sourceforge。nettcltcl8。6。1src。tar。gz。。。。。。解压。。。。。。cd。unix。。。。。。安装即可。。。。。。 安装过程中还可能有其它问题,按照报错解决就可以了 这些问题解决后,就可以安装Redis3。2的版本了:makeinstall。。。。。。Hint:I)。。。。。。INSTALLinstallINSTALLinstallINSTALLinstallINSTALLinstallINSTALLinstall。。。。。。 22、客户端连接测试 Redis的客户端组件可以和Spring框架无缝集成,还兼容多种开发语言:如PHP、Go、C、python、C、NodeJS等等。下面我们单纯使用Redis的Java客户端组件Jedis,完成一个简单的Redis服务端连接,并向服务器写入一个KeyValue值。我们使用的Jedis版本为2。8,您首先需要在Maven工程中引入这个组件:。。。。。。dependencygroupIdredis。clientsgroupIdjedisartifactIdversion2。8。0versiondependency。。。。。。 以下是Java测试程序的主要代码片段:。。。。。。JedisredisClientnewJedis(192。168。61。140,6379);写入一个KV值redisClient。set(key1,value);redisClient。close();。。。。。。 通过以上代码,我们向Redis服务器写入了一个KV键值对,那么这个操作是否成功了呢?我们可以使用Redis自带的Client功能进行查看:redisclih192。168。61。140192。168。61。140:6379getkey1value Redis自带的Client功能非常强大,包括写入、查询、监控、调试等,从以上的get命令结果来看Java客户端代码上的写入操作是成功了。但那只是最简单的写入操作,Redis支持的多种复杂数据结构以及他们的组织方式,我们将在后文进行详细讨论。 3、基本配置参数 Redis3。2版本中,最重要的配置文件就是存在于安装目录中的名叫redis。conf的文件。其中的范本格式清楚说明了Redis3。2版本中可以配置的主要信息和含义。主要包括几个方面:网络配置项、一般配置项、快照配置项、复制(高可用)配置项、集群配置项、安全配置项、资源限制配置项、LUA脚本配置、慢日志配置、监控配置、事件通知配置、数据结构高级配置等。 本节内容将首先讨论一些基础配置项,后续文章中在介绍Redis对各种数据结构支持时再介绍数据结构相应的配置项,介绍Redis数据快照原理时再介绍快照配置项,介绍Redis高可用方案和集群方案时再介绍相应的配置选项。 31、网络配置项和一般配置项bind:绑定的主机地址,不设置默认将处理所有请求。由于一台主机可能有多个IP,如果这里设置192。168。1。100,那么Redis只会处理达到这个IP的数据请求。protectedmode:设置是否开启网络保护模式,默认为yes(开启)。这个选项的工作方式是,如果您没有设置了bind参数,也没有设置安全登录密码,则当这个参数开启时Redis只允许本地连接的客户端访问。也就是说如果您不设置bind参数,而又需要其它客户端通过网络访问,那么就设置这个参数为no(关闭)吧。port:Redis服务的工作端口,默认为6379。tcpbacklog:在Linux服务器上有一个配置参数net。core。somaxconn,这是参数表示socket监听(listen)的backlog上限。所谓backlog是指已经和服务器完成了TCP三次握手确认,但是还没有被上层应用程序处理的请求队列。这个参数在操作系统层面的默认值是128,很显然在压力非常大的系统上,这个值小了一点。您可以修改成2048或者更大,而为Redis配置的tcpbacklog应该小于或者等于您在操作系统上设置的最大限制值(否则Redis上面设置再大也没有用)。net。core。somaxconn参数的调整可参见其它资料。timeout:该参数表示当某一个客户端连接上来并闲置timeout(单位秒)的时间后,Redis服务端就主动关闭这个客户端连接。该配置参数的默认值为0,表示关闭这个功能。tcpkeepalive:客户端TCP连接的健康性检查,如果不设置为0就表示Redis服务端会定时发送SOKEEPALIVE心跳机制检测客户端的反馈情况。该配置的默认值为300秒,既是300秒检测一次。健康性检查的好处是,在客户端异常关闭的情况下,Redis服务端可以发现这个问题,并主动关闭对端通道。这个参数建议开启。daemonize:当为yes的时候,以守护进程的模式运行。该参数的默认值为no,主要目的是为了在测试环境下调试方便;当运行在生产环境时,可以将这个选项配置为yes。pidfile:当Redis没有以守护进程的方式运行,这个pid文件的配置选项不会起作用。当redis以守护进程方式运行时,这个配置选项设置的文件就会被创建和使用。loglevel:配置的Redis日志级别,默认为notice。这个选项可以设置以下这些值:debug,非常多的信息无论有用的还是没用的都会被显示或者记录,一般用于开发和测试状态;verbose,比起debug要少很多,但是也极少出现有用的信息;notice,信息量适度而且一般不会错过有用的信息,可用于生产环境;warning,只有非常有用和危险的信息会被显示或者记录,建议使用warning级别的日志记录模式。logfile:日志文件存在的位置,默认为空字符串设置,这时日志会打印到标准输出设备。syslogenabled:默认为no(开启),当配置为yes时,Redis的日志会输出到系统日志。databases:从Redis很早的版本开始,它就支持多个独立的内存数据库同时存在。该值设置数据库的数量,默认数据库为16,可以使用SELECT命令在连接上指定数据库id。 32、安全和资源限制配置项 requirepass:该选项指定客户端在完成连接后,在执行任何其它命令前必须使用的鉴权密码。这在技术团队需要拒绝一些不信任的客户端访问Redis服务的情况下,是比较有用的。 renamecommand:在生产环境下,有一些Redis命令是非常危险的,例如FLUSHALL命令和FLUSHDB命令。所以Redis服务器为生产环境的指令安全性提供了一个重命名功能,通过这个功能我们可以将一些需要避免执行的指令变更成其它指令。例如以下设定可以将FLUSHALL指令变更成空字符:。。。。。。renamecommandFLUSHALLrenamecommandFLUSHDB。。。。。。 这是再使用Redis的客户端执行以上命令,就会收到以下的提示:。。。。。。开启了密码功能,并设置密码为123456redisclih192。168。61。140a123456192。168。61。140:6379FLUSHDB(error)ERRunknowncommandFLUSHDB192。168。61。140:6379FLUSHALL(error)ERRunknowncommandFLUSHALL。。。。。。maxclients:设定当前同时连接到本Redis服务上的客户端数量,默认为10000。注意这个值的设定还和操作系统上filelimit参数有关联,实际情况是10000个同时正常的客户端连接完全够用,如果实际情况需要调整则一定要同时确认该设定值没有超过操作系统上设定的文件描述符限制数量。maxmemory:该选项是告诉Redis当使用了多少物理内存后就开始拒绝后续的写入请求,该参数能很好的保护好你的Redis不会因为使用了过多的物理内存而导致swap,最终严重影响性能甚至崩溃。该参数的设置单位为bytes,建议的设置范围不要超过操作系统可支配内存的35,例如您的操作系统可支配8GB内存,那么建议的设置值就是5GB,也就是5242880k、5120mb、5gb。在配置容量大小时,Redis支持各种单位简写,也就是说1GB1Gb1gB都是一个意思。如果不设置该参数就表示不限制,但这种方式在生产环境下不推荐。maxmemorypolicy:这个参数非常重要,它设置了当内存达到最大可用数量时的内存清除策略。该选项可以设置以下这些值:volatilelru设定值,使用Redis时如果不是有特殊的存储要求,都建议设定Key的过期时间expire。一旦设置了过期时间Redis会将这些Key放置到一个LRU队列中(关于LRU队列算法的内容可以参考本专题之前的文章),Redis使用惰性删除定期删除的方式对LRU队列中过期的Key进行清理。当内存达到满载状态时,Redis也会启动一次清除操作。allkeyslru设定值将忽略Key的过期时间设定,将所有Key放入LRU队列并清除过期的数据。volatilerandom该选项将对过期的Key值采取随即选取算法进行清除。allkeysrandom选项和volatilerandom选项类似,只不过将Key的选取范围扩大到了所有Key,而不只针对设定了过期时间expire的Key。volatilettl,该选项将清除当前即将过期的Key(最小TTL算法)。noeviction,如果清除策略设置该值,在Redis内存满载情况下将不启动任何清除策略,直接拒绝后续的写操作。noeviction是一个默认策略,所以建议在生产环境中一定要进行清除策略的修改。maxmemorysamples:以上提到的LRU算法和TTL算法在Redis中的实现并不是固定的,Redis支持技术人员根据实际情况在算法性能和精度间进行调整。maxmemorysamples配置项实际上就是一个Key的抽样值,值越高算法精度越高。例如设置为10的时候,Redis中的LRU算法基本上处理失效的Key就非常精确了,但是却需要消耗较多的CPU资源;设置为3的时候,虽然处理失效的Key精度不高,但是处理速度非常快。该选项的默认值为5。 33、日志和监控配置项 331、慢日志 Redis从很早的版本就提供了一个慢操作的日志记录功能,称为慢日志。当Redis中某一个内部操作所耗费的时间超过了设定的一个限制阀值,这个操作就会被慢日志功能记录在内存中。注意这里提到的是内部操作时间,并不包括可能的网络IO操作时间。 通过Redis的客户端使用slowlogget、slowloglen、slowlogreset等命令可以查看当然Redis服务端记录的慢日志情况。Redis的配置信息中提供了两个参数供技术团队调整慢日志功能:slowloglogslowerthan和slowlogmaxlen。 slowloglogslowerthan:这个参数就是给定慢日志的时间阀值,单位为微妙。也就是说1000000等于1秒。该参数的默认为10000。请注意,如果该参数的值设置为负数,将禁用慢日志功能;如果设置为0,将会记录每一条操作日志。 slowlogmaxlen:这个参数存储Redis服务端能够记录的慢日志总长度。这个数值没有上限,只是需要技术团队注意这会消耗宝贵的内存资源,实际上默认的128就是比较实用的设置值。 332、延迟监控功能 LATENCY监控器功能,是从RedisVersion2。8。3开始提供的一个重要的反映其延迟状态的监控功能。其工作任务是当技术人员设置了一个延迟操作的时间阀值后,凡是超过了这个阀值时间的操作都将被认为是执行缓慢的操作被记录下来。Redis客户端在登录后,可以使用诸如LATENCYLATEST、LATENCYHISTORY、LATENCYRESET、LATENCYGRAPH、LATENCYDOCTOR等命令控制查询LATENCY监控器。LATENCY监控器功能更详细的介绍,可以参看Redis官方文档中的介绍:https:redis。iotopicslatencymonitor。 latencymonitorthreshold,这个参数项用来设置操作时间的阀值,如果关闭延迟监控功能的话,可以将这个值设置为0。另外客户端连接可以独立设置这个阀值,只需要在连接Redis服务端成功后,执行CONFIGSETlatencymonitorthresholdXXXX命令即可。 二、Redis事件订阅和持久化存储 34、事件功能和配置项 Redis从2。X版本开始,就支持一种基于非持久化消息的、使用发布订阅模式实现的事件通知机制。所谓基于非连接保持,是因为一旦消息订阅者由于各种异常情况而被迫断开连接,在其重新连接后,其离线期间的事件是无法被重新通知的(一些Redis资料中也称为即发即弃)。而其使用的发布订阅模式,意味着其机制并不是由订阅者周期性的从Redis服务拉取事件通知,而是由Redis服务主动推送事件通知到符合条件的若干订阅者。 Redis中的事件功能可以提供两种不同的功能。一类是基于Channel的消息事件,这一类消息和Redis中存储的Keys没有太多关联,也就是说即使不在Redis中存储任何Keys信息,这类消息事件也可以独立使用。另一类消息事件可以对(也可以不对)Redis中存储的Keys信息的变化事件进行通知,可以用来向订阅者通知Redis中符合订阅条件的Keys的各种事件。Redis服务的事件功能在实际场景中虽然使用得不多,不过还是可以找到案例,例如服务治理框架DUBBO默认情况下使用Zookeeper作为各节点的服务协调装置,但可以通过更改DUBBO的配置,将Zookeeper更换为Redis。 341、publish和subscribe 我们先从比较简单的publish命令和subscribe命令开始介绍,因为这组命令所涉及到的Channel(通道)和Redis中存储的数据相对独立。publish命令由发送者使用,负责向指定的Channel发送消息;subscribe命令由订阅者使用,负责从指定的一个或者多个Channel中获取消息。 以下是publish命令和subscribe命令的使用示例:该命令向指定的channel名字发送一条消息(字符串)PUBLISHchannelmessage例如:向名叫FM955的频道发送一条消息,消息信息为hello!PUBLISHFM955hello!再例如:向名叫FM900的频道发送一条消息,消息信息为doit!PUBLISHFM900doit!该命令可以开始向指定的一个或者多个channel订阅消息SUBSCRIBEchannel〔channel。。。〕例如:向名叫FM955的频道订阅消息SUBSCRIBEFM955再例如:向名叫FM955、FM900的两个频道订阅消息SUBSCRIBEFM955FM900 如果您使用需要使用publish命令和subscribe命令,您并不需要对Redis服务的配置信息做任何更改。以下示例将向读者展示两个命令的简单使用方式前提是您的Redis服务已经启动好了: 由客户端A充当订阅者,在ChannelA和ChannelB两个通道上订阅消息 我们使用的Redis服务地址为192。168。61。140,端口为默认值〔rootkp2〕redisclih192。168。61。140192。168。61。140:6379SUBSCRIBEChannelAChannelBReadingmessages。。。(pressCtrlCtoquit)1)subscribe2)ChannelA3)(integer)11)subscribe2)ChannelB3)(integer)2 有客户端B从当订阅者,通过ChannelB发送消息给所有订阅者。 连接到Redis服务器后,直接运行PUBLISH命令,发送信息〔rootkp1〕redisclih192。168。61。140192。168。61。140:6379PUBLISHChannelBhello(integer)1 以下是订阅者客户端A所受到的message信息:。。。。。。这时订阅者收到消息如下:1)message2)ChannelB3)hello 从以上示例中可以看到,客户端A确实收到了客户端B所发送的消息信息,并且收到三行信息。这三行信息分别表示消息类型、消息通道和消息内容。注意,以上介绍的这组publish命令和subscribe命令的操作过程并没有对Redis服务中已存储的任何Keys信息产生影响。 342、模式订阅psubscribe Redis中还支持一种模式订阅,它主要依靠psubscribe命令向技术人员提供订阅功能。模式订阅psubscribe最大的特点是,它除了可以通过Channel订阅消息以外,还可以配合配置命令来进行Keys信息变化的事件通知。 模式订阅psubscribe的Channel订阅和subscribe命令类似,这里给出一个命令格式,就不再多做介绍了(可参考上文对subscribe命令的介绍):该命令可以开始向指定的一个或者多个channel订阅消息具体使用示例可参见SUBSCRIBE命令PSUBSCRIBEchannel〔channel。。。〕 模式订阅psubscribe对Keys变化事件的支持分为两种类型:keyspace(键空间通知)和keyevent(键事件通知),这两类事件都是依靠Key的变化触发的,而关键的区别在于事件描述的焦点,举例说明: 当Redis服务中0号数据库的MyKey键被删除时,键空间和键事件向模式订阅者分别发送的消息格式如下:以下命令可订阅键空间通知订阅0号数据库任何Key信息的变化192。168。61。140:6379psubscribekeyspace0:Readingmessages。。。(pressCtrlCtoquit)1)psubscribe2)keyspace0:3)(integer)1出现以上信息,说明订阅成功当其他客户端执行setmykey123456时,该订阅可收到以下信息1)pmessage2)keyspace0:3)keyspace0:mykey4)set 以上收到的订阅信息,其描述可以概括为:mykey的键空间发生了事件,事件为set。这样的事件描述着重于key的名称,并且告诉客户端key的事件为set。我们再来看看订阅键事件通知时,发生同样事件所得到的订阅信息:以下命令可订阅键事件通知订阅0号数据库任何事件的变化192。168。61。140:6379psubscribekeyevent0:Readingmessages。。。(pressCtrlCtoquit)1)psubscribe2)keyevent0:3)(integer)1出现以上信息,说明订阅成功当其他客户端执行setmykey123456时,该订阅可收到以下信息1)pmessage2)keyevent0:3)keyevent0:set4)mykey 以上收到的订阅信息中事件是主体,其信息可以概括为:0号数据库发生了set事件,发生这个事件的key信息为mykey。 343、模式订阅的配置 a、配置和通配符 要使用psubscribe命令进行键事件的订阅,就首先需要在Redis的主配置文件中对模式订阅进行设定。注意,如果您只是使用psubscribe命令通过Channel发送消息到订阅者,或者更单纯的使用publish命令和subscribe命令组合通过Channel发送和接收消息,就不需要进行这样的配置。 默认情况下Redis服务下的键空间通知和键事件通知都是关闭的。在redis。conf文件下,有专门的EVENTNOTIFICATION区域进行设定,设置的格式为:。。。。。。notifykeyspaceevents〔通配符〕。。。。。。 通配符的定义描述如下:K:启用keyspace键空间通知,客户端可以使用keyspace为前缀的格式使用订阅功能。E:启用keyevent键事件通知,客户端可以使用keyevent为前缀的格式使用订阅功能。g:监控一般性事件,包括但不限于对del,expire,rename事件的监控。:启用对字符串格式(即一般KV结构)命令的监控。l:启用对List数据结构命令的监控。s:启用对Set数据结构命令的监控。h:启用对Hash数据结构命令的监控。z:启用对ZSet数据结构命令的监控。x:启用对过期事件的监控。e:启用对驱逐事件的监控,当某个键因maxmemory达到设置时,使用策略进行内存清理,会产生这个事件。A:glshzxe通配符组合的别名,也就是说AKE这样的通配符组合,意味着所有事件。 以下的几个实例说明了配置格式中通配符的用法:监控任何数据格式的所有事件,包括键空间通知和键事件通知notifykeyspaceeventsAKE只监控字符串结构的所有事件,包括键空间通知和键事件通知notifykeyspaceeventsgKExe只监控所有键事件通知notifykeyspaceeventsAE只监控Hash数据解构的键空间通知notifykeyspaceeventsghKxe只监控Set数据结构的键事件通知notifykeyspaceeventsgsExe 注意,在Redis主配置文件中进行事件通知的配置,其配置效果是全局化的。也就是说所有连接到Redis服务的客户端都会使用这样的Key事件通知逻辑。但如果单独需要为某一个客户端会话设置独立的Key事件通知逻辑,则可以在客户端成功连接Redis服务后,使用类似如下的命令进行设置:。。。。。。192。168。61。140:6379configsetnotifykeyspaceeventsKEAOK b、键事件订阅 完成键事件的配置后,就可以使用psubscribe命令在客户端订阅消息通知了。这个过程还是需要使用通配符参数,才能完成订阅指定。通配符格式如下所示:psubscribe〔keyspacekeyevent〕db:〔prefix〕例如:订阅0号数据库中,所有的键变化事件,进行键空间通知psubscribekeyspace0:订阅0号数据库,所有的键变化事件,进行键空间通知和键事件通知psubscribekey0: 注意,就如上文所提到的那样,客户端能够进行键信息变化事件订阅的前提是Redis服务端或者这个客户端会话本身开启了相应配置。以下举例说明psubscribe命令中参数的使用方式:注意,Redis服务上的配置信息如下notifykeyspaceeventsgsExe即是说只允许监控Set结构的所有事件,并且之启用了键事件通知,没有启用键空间通知。客户端使用以下命令开始订阅Key的变化事件192。168。61。140:6379psubscribekey0:以上命令订阅了0号数据库所有键信息的变化通知,包括键事件通知和键空间通知Readingmessages。。。(pressCtrlCtoquit)1)psubscribe2)key0:3)(integer)1接着,已连接到Redis服务上的另一个客户端执行了如下命令saddmysetkeyrt那么收到的消息通知为1)pmessage2)key0:3)keyevent0:sadd4)mysetkey 以上实例操作中有两个问题需要单独进行说明:当客户端使用psubscribe命令进行订阅时(psubscribekey0:),实际上是连同keyspace(键空间通知)和keyevent(键事件通知)一起订阅了。那么按照上文介绍的内容来说,这个订阅者本该收到两条事件消息。一条消息的描述重点在key上,另一条消息的描述重点在sadd事件上。但实际情况是,这个订阅者只收到了以描述重点在事件上的键事件通知。这是因为在以上实例中特别说明的一点:Redis服务端只开启键事件通知的配置。所以无论客户端如何订阅键空间通知,也收不到任何消息。另外,包括Redis官方资料在内的资料都在阐述这样一个事实,既是通过sadd命令对一个Set结构中的元素进行变更和直接通过PUBLISHkeyevent0:saddmysetkey这样的命令向订阅者发送消息,在消息订阅者看来效果都是一样。但是这两种不同的操作过程对于Redis存储的Key数据,则是完全不一样的。前者的操作方式会改变Redis中存储的数据状况,但后者则不会。 344、Redis订阅发布功能的不足 Redis提供的订阅发布功能并不完美,更不能和ActiveMQRabbitMQ提供的订阅发布功能相提并论。首先这些消息并没有持久化机制,属于即发即弃模式。也就是说它们不能像ActiveMQ中的消息那样保证持久化消息订阅者不会错过任何消息,无论这些消息订阅者是否随时在线。由于本来就是即发即弃的消息模式,所以Redis也不需要专门制定消息的备份和恢复机制。也是由于即发即弃的消息模式,所以Redis也没有必要专门对使用订阅发布功能的客户端连接进行识别,用来明确该客户端连接的ID是否在之前已经连接过Redis服务了。ActiveMQ中保持持续通知的功能的前提,就是能够识别客户端连接ID的历史连接情况,以便确定哪些订阅消息这个客户端还没有处理。Redis当前版本有一个简单的事务机制,这个事务机制可以用于PUBLISH命令。但是完全没有ActiveMQ中对事务机制和ACK机制那么强的支持。而在我写作的系统间通讯专题中,专门讲到了ActiveMQ的ACK机制和事务机制。Redis也没有为发布者和订阅者准备保证消息性能的任何方案,例如在大量消息同时到达Redis服务是,如果消息订阅者来不及完成消费,就可能导致消息堆积。而ActiveMQ中有专门针对这种情况的慢消息机制。 35、Redis持久化存储 从严格意义上说,Redis服务提供四种持久化存储方案:RDB、AOF、虚拟内存(VM)和DISKSTORE。虚拟内存(VM)方式,从RedisVersion2。4开始就被官方明确表示不再建议使用,Version3。2版本中更找不到关于虚拟内存(VM)的任何配置范例,Redis的主要作者SalvatoreSanfilippo还专门写了一篇论文,来反思Redis对虚拟内存(VM)存储技术的支持问题。 至于DISKSTORE方式,是从RedisVersion2。8版本开始提出的一个存储设想,到目前为止Redis官方也没有在任何stable版本中明确建议使用这用方式。在Version3。2版本中同样找不到对于这种存储方式的明确支持。从网络上能够收集到的各种资料来看,DISKSTORE方式和RDB方式还有这一些千丝万缕的联系,不过各位读者也知道,除了官方文档以外网络资料很多就是大抄。 最关键的是目前官方文档上能够看到的Redis对持久化存储的支持明确的就只有两种方案(https:redis。iotopicspersistence):RDB和AOF。所以本文也只会具体介绍这两种持久化存储方案的工作特定和配置要点。 351、RDB RDB中文名为快照内存快照,它的过程很好理解,就是Redis按照一定的时间周期将目前服务中的所有数据全部写入到磁盘中。但这个过程说起简单,实际上呢有很多细节需要被处理。Redis主配置文件的REPLICATION部分,放置了对这个过程的配置选项。在我们后续文章中讲解Redis支持的主从复制时,也可以看到RDB的影子。 上图反映了内存快照的大致过程,由于生产环境中我们为Redis开辟的内存区域都比较大(例如6GB),那么将内存中的数据同步到硬盘的过程可能就会持续比较长的时间,而实际情况是这段时间Redis服务一般都会收到数据写操作请求。那么如何保证数据一致性呢?RDB中的核心思路是CopyonWrite,来保证在进行快照操作的这段时间,需要压缩写入磁盘上的数据在内存中不会发生变化。在正常的快照操作中,一方面Redis主进程会fork一个新的快照进程专门来做这个事情,这样保证了Redis服务不会停止对客户端包括写请求在内的任何响应。另一方面这段时间发生的数据变化会以副本的方式存放在另一个新的内存区域,待快照操作结束后才会同步到原来的内存区域。 另一个问题是,在进行快照操作的这段时间,如果发生服务崩溃怎么办?很简单,在没有将数据全部写入到磁盘前,这次快照操作都不算成功。如果出现了服务崩溃的情况,将以上一次完整的RDB快照文件作为恢复内存数据的参考。也就是说,在快照操作过程中不能影响上一次的备份数据。Redis服务会在磁盘上创建一个临时文件进行数据操作,待操作成功后才会用这个临时文件替换掉上一次的备份。 以下是Redis中关于内存快照的主要配置信息:快照周期:内存快照虽然可以通过技术人员手动执行SAVE或BGSAVE命令来进行,但生产环境下多数情况都会设置其周期性执行条件。Redis中默认的周期新设置如下:周期性执行条件的设置格式为savesecondschanges默认的设置为:save9001save30010save6010000以下设置方式为关闭RDB快照功能save 以上三项默认信息设置代表的意义是:如果900秒内有1条Key信息发生变化,则进行快照;如果300秒内有10条Key信息发生变化,则进行快照;如果60秒内有10000条Key信息发生变化,则进行快照。读者可以按照这个规则,根据自己的实际请求压力进行设置调整。stopwritesonbgsaveerror:上文提到的在快照进行过程中,主进程照样可以接受客户端的任何写操作的特性,是指在快照操作正常的情况下。如果快照操作出现异常(例如操作系统用户权限不够、磁盘空间写满等等)时,Redis就会禁止写操作。这个特性的主要目的是使运维人员在第一时间就发现Redis的运行错误,并进行解决。一些特定的场景下,您可能需要对这个特性进行配置,这时就可以调整这个参数项。该参数项默认情况下值为yes,如果要关闭这个特性,指定即使出现快照错误Redis一样允许写操作,则可以将该值更改为no。rdbcompression:该属性将在字符串类型的数据被快照到磁盘文件时,启用LZF压缩算法。Redis官方的建议是请保持该选项设置为yes,因为it’salmostalwaysawin。rdbchecksum:从RDB快照功能的version5版本开始,一个64位的CRC冗余校验编码会被放置在RDB文件的末尾,以便对整个RDB文件的完整性进行验证。这个功能大概会多损失10左右的性能,但获得了更高的数据可靠性。所以如果您的Redis服务需要追求极致的性能,就可以将这个选项设置为no。dbfilename:RDB文件在磁盘上的名称。dir:RDB文件的存储路径。默认设置为。,也就是Redis服务的主目录。 352、AOF 由于是周期性的同步,所以RDB存在的最大问题就是在Redis异常崩溃,需要从最近一次RDB文件恢复数据时,常常出现最近一批更新的数据丢失,而且根据快照的周期设置,这批数据的总量还可能比较大。另外,虽然使用专门的快照进程进行快照数据同步的方式,本身不会造成Redis服务出现卡顿。但如果需要快照的数据量特别大,操作系统基本上会将CPU资源用到快照操作上去,这可能间接造成包括Redis主进程在内的其它进程被挂起。所以,以一个较大的时间周期全部同步Redis数据状态的快照方式,在非常高并发的情况下并不是最好的解决方法。 虽然RDB快照基本上可以应付我们遇到的大多数业务场景,也可以满足至少80业务系统设计时的预想性能压力,但为了尽可能解决RDB的工作缺陷,Redis还是提供了另一种数据持久化方式AOF。AOF全称是AppendOnlyFile,从字面上理解就是只进行增加的文件。在本专题中,我们在介绍InnoDB的工作过程时,也介绍了类似只进行增加的文件,就是InnoDB中最关键的重做日志。 在物理磁盘的操作无论是机械磁盘还是固态磁盘,使用顺序读写都将获得比随机读写好得多的IO性能。所以我们可以看到无论是关系型数据库、NoSQL数据库还是之前的业务案例,无一例外都追求在物理磁盘上尽可能进行顺序读写操作。 AOF方式的核心设计思量可以总结为一句话:忠实记录Redis服务启动成功后的每一次影响数据状态的操作命令,以便在Redis服务异常崩溃的情况出现时,可以按照这些操作命令恢复数据状态。既然要记录每次影响数据状态的操作命令,就意味着AOF文件会越来越大!这是必然的。还好Redis为AOF提供了一种重写AOF文件的功能,保证了AOF文件中可以存储尽可能少的操作命令就能保证数据恢复到最新状态,这个功能被称为日志重写功能(请注意这可不是我们在讲解InnoDB时提到的重做日志)。 举个例子,操作人员在Redis中设置了一个KV结构:mykey3yinwenjie,之后有删除了这个Key信息。那么AOF文件中记录的动作可能如下所示(AOF文件中的内容可以直接通过各种文本编辑工具直接查看):cat。appendonly。aof。。。。。。33SET6mykey312yinwenjie111。。。。。。23del6mykey3。。。。。。 可以看到以上AOF文件中的内容,如实记录了这两个操作:设置Key和删除Key。但是这种日志记录过程对恢复Key的信息没有任何帮助,因为mykey3这个Key信息注定在最新的Redis内存中是不存在的。所以一旦我们运行重写日志命令(可以是设定的条件也可以直接运行BGREWRITEAOF命令),那么整理后的AOF文件的内容可能就是如下所示了:cat。appendonly。aof。。。。。。。。。。。。 在AOF文件中对mykey3这个Key信息的操作过程记录消失了!这不但缩小了AOF文件还没有对数据恢复过程造成任何困扰。Redis主配置文件中关于AOF功能的设定可以在APPENDONLYMODE部分找到:appendonly:默认情况下AOF功能是关闭的,将该选项改为yes以便打开Redis的AOF功能。appendfilename:这个参数项很好理解了,就是AOF文件的名字。appendfsync:这个参数项是AOF功能最重要的设置项之一,主要用于设置正真执行操作命令向AOF文件中同步的策略。什么叫正真执行呢?还记得我们在本专题中介绍的Linux操作系统对磁盘设备的操作方式吗?为了保证操作系统中IO队列的操作效率,应用程序提交的IO操作请求一般是被放置在LinuxPageCache中的,然后再由Linux操作系统中的策略自行决定正在写到磁盘上的时机。而Redis中有一个fsync()函数,可以将PageCache中待写的数据真正写入到物理设备上,而缺点是频繁调用这个fsync()函数干预操作系统的既定策略,可能导致IO卡顿的现象频繁。 appendfsync参数项可以设置三个值,分别是:always、everysec、no,默认的值为everysec。 always参数值,会使得AOF对数据的保存非常稳健。其设置意义是只要有一个写操作命令执行成功,就执行一次fsync函数调用。所以很显然always的设定值,就是三个选项值中处理效率最慢的。 no参数值,这个设置值表示Redis不会将执行成功的操作命令正真刷入AOF文件,而是完成操作系统级别的写操作后就认为AOF文件记录成功了,后续的IO操作完全依赖于操作系统的设定,一般30秒会刷一次。 everysec参数值,这是默认的设置值,也是可以在数据稳健性和性能上平衡较好策略。它表示每秒钟都做一次fsync函数调用,正真做AOF文件的写入操作。noappendfsynconrewrite:always和everysec的设置会使正真的IO操作高频度的出现,甚至会出现长时间的卡顿情况,这个问题出现在操作系统层面上,所有靠工作在操作系统之上的Redis是没法解决的。为了尽量缓解这个情况,Redis提供了这个设置项,保证在完成fsync函数调用时,不会将这段时间内发生的命令操作放入操作系统的PageCache(这段时间Redis还在接受客户端的各种写操作命令)。autoaofrewritepercentage:上文说到在生产环境下,技术人员不可能随时随地使用BGREWRITEAOF命令去重写AOF文件。所以更多时候我们需要依靠Redis中对AOF文件的自动重写策略。Redis中对触发自动重写AOF文件的操作提供了两个设置:autoaofrewritepercentage表示如果当前AOF文件的大小超过了上次重写后AOF文件的百分之多少后,就再次开始重写AOF文件。例如该参数值的默认设置值为100,意思就是如果AOF文件的大小超过上次AOF文件重写后的1倍,就启动重写操作。autoaofrewriteminsize:参考autoaofrewritepercentage选项的介绍,autoaofrewriteminsize设置项表示启动AOF文件重写操作的AOF文件最小大小。如果AOF文件大小低于这个值,则不会触发重写操作。注意,autoaofrewritepercentage和autoaofrewriteminsize只是用来控制Redis中自动对AOF文件进行重写的情况,如果是技术人员手动调用BGREWRITEAOF命令,则不受这两个限制条件左右。 353、持久化存储的性能建议 关于持久化存储的性能建议,我们将结合后文介绍的Redis集群方案一起进行分析 三、Redis集群方案:高可用 1、概述 从本篇文章开始,我们将向读者介绍几种Redis的高可用高负载集群方案。除了介绍Redis3。X版本中推荐的原生集群方案外,还会介绍使用第三方组件搭建Redis集群的方法。本文我们会首先介绍Redis的高可用集群方案。 2、Redis高可用方案 Redis提供的高可用方案和我们介绍过的很多软件的高可用方案类似,都是使用主从节点的思路。即是有一个Master节点在平时提供服务,另外一个或多个Slave节点在平时不提供服务(或只提供数据读取服务)。当Master节点由于某些原因停止服务后,再人工自动完成Slave节点到Master节点的切换工作,以便整个Redis集群继续向外提供服务。既然是要进行角色切换,且要求这些节点针对外部调用者来说没有任何不同,最重要的就是Master节点和Slave节点的数据同步过程。数据同步最关键的设计思路是如何在数据一致性和同步性能上找到一个完美的平衡点。 同步复制的工作思路可以概括为:Master节点的任何数据变化都会立即同步到一个或多个Slave节点上。只要一个Slave节点同步失败(例如超时),都会认为整个数据写操作过程失败。这样的设计考虑侧重于保证各节点上的数据绝对一致,完全没有考虑对Master节点的响应性能,甚至会出现Master节点为了保证数据一致性而停止对后续写操作请求的响应。 异步复制的工作思路可以概括为:Master节点首先保证对外部请求的响应性能,它和Slave节点的数据同步一般由一个新的进程线程独立完成。数据复制过程由Slave节点周期性发起或者由它一直驻留在Master节点的连接进行实时监控又或者由Master节点主动推送数据,再或者是同时使用多个异步复制过程。由于在Slave节点进行数据同步时,Master节点一直在处理新的数据写请求,所以Slave节点已完成同步的数据和Master上的实时数据一般会存在一些差异。例如MySQL原生支持的数据复制过程,就是一个异步过程。 很显然异步复制思路在对调用者的响应性能上,表现要比同步复制好得多。但如果由于异步复制而导致的节点间数据差异达到某种程度,就失去了数据同步的意义了。所以如何减少节点间的数据差异就成为异步复制过程中需要关注的要点。而后者的处理办法就有很多了,例如MySQL由第三方插件支持的半同步方式,又例如讲解ActiveMQ消息队列时提到的AutoAck和DUPSOKACK,再例如我们下文介绍的DisklessReplication和Master写保护。 21、主从复制工作过程 Redis的主从复制功能除了支持一个Master节点对应多个Slave节点的同时进行复制外,还支持Slave节点向其它多个Slave节点进行复制。这样就使得架构师能够灵活组织业务缓存数据的传播,例如使用多个Slave作为数据读取服务的同时,专门使用一个Slave节点为流式分析工具服务。Redis的主从复制功能分为两种数据同步模式:全量数据同步和增量数据同步。 上图简要说明了Redis中Master节点到Slave节点的全量数据同步过程。当Slave节点给定的runid和Master的runid不一致时,或者Slave给定的上一次增量同步的offset的位置在Master的环形内存中无法定位时(后文会提到),Master就会对Slave发起全量同步操作。这时无论您是否在Master打开了RDB快照功能,它和Slave节点的每一次全量同步操作过程都会更新创建Master上的RDB文件。在Slave连接到Master,并完成第一次全量数据同步后,接下来Master到Slave的数据同步过程一般就是增量同步形式了(也称为部分同步)。增量同步过程不再主要依赖RDB文件,Master会将新产生的数据变化操作存放在一个内存区域,这个内存区域采用环形构造。过程如下: 为什么在Master上新增的数据除了根据Master节点上RDB或者AOF的设置进行日志文件更新外,还会同时将数据变化写入一个环形内存结构,并以后者为依据进行Slave节点的增量更新呢?主要原因有以下几个: 由于网络环境的不稳定,网络抖动延迟都可能造成Slave和Master暂时断开连接,这种情况要远远多于新的Slave连接到Master的情况。如果以上所有情况都使用全量更新,就会大大增加Master的负载压力写RDB文件是有大量IO过程的,虽然LinuxPageCahe特性会减少性能消耗。 另外在数据量达到一定规模的情况下,使用全量更新进行和Slave的第一次同步是一个不得已的选择因为要尽快减少Slave节点和Master节点的数据差异。所以只能占用Master节点的资源和网络带宽资源。 使用内存记录数据增量操作,可以有效减少Master节点在这方面付出的IO代价。而做成环形内存的原因,是为了保证在满足数据记录需求的情况下尽可能减少内存的占用量。这个环形内存的大小,可以通过replbacklogsize参数进行设置。 Slave重连后会向Master发送之前接收到的Masterrunid信息和上一次完成部分同步的offset的位置信息。如果Master能够确定这个runid和自己的runid一致且能够在环形内存中找到这个offset的位置,Master就会发送从offset的位置开始向Slave发送增量数据。那么连接正常的各个Slave节点如何接受新数据呢?连接正常的Slave节点将会在Master节点将数据写入环形内存后,主动接收到来自Master的数据复制信息。 22、基本MasterSlave配置 Redis提供的主从复制功能的配置信息,在Redis主配置文件的REPLICATION部分。以下是这个部分的主要参数项说明:slaveof:如果您需要将某个节点设置为某个Master节点的Slave节点,您需要在这里指定Master节点的IP信息和端口信息。这个设置项默认是关闭的,也即是说Master节点不需要设置这个参数。另外,除了通过配置文件设置外,您还可以通过Redis的客户端命令进行slaveof设定。lislaveservestaledata:当master节点断开和当前salve节点的连接或者当前slave节点正在进行和master节点的数据同步时,如果收到了客户端的数据读取请求,slave服务器是否使用陈旧数据向客户端提供服务。该参数的默认值为yes。slavereadonly是否将salve节点设置为只读。一旦设置为只读,表示这个Salve节点只会进行数据读取服务,如果客户端直接向这个Salve节点发送写数据的请求,则会收到错误提示。建议采用默认的yes值进行设定。repldisklesssync:上文已经介绍过Redis的主从复制功能基于RDB,后者的过程是将数据刷入RDB文件(实际上是Linux的PageCache区域),然后基于RDB文件内容的更新情况和Salve当前已同步的数据标记点来进行Salve上的数据更新。所以这个过程实际会增加一定的数据延迟,消耗一定的处理资源。基于这个情况,Redis中提供了一种不经过物理磁盘设备就进行主从数据同步的技术,称为diskless。但是直到Redisversion3。2这个技术也一直处于试验状态,所以并不推荐在生产环境下使用:WARNING:DISKLESSREPLICATIONISEXPERIMENTALCURRENTLY。repldisklesssyncdelay:这个参数只有在上一个参数设置为yes时才起作用,主要是设置在进行两次diskless模式的数据同步操作的时间间隔。默认为5秒。replpingslaveperiod:Slave节点向Master节点发送ping指令的事件间隔,默认为10秒。repltimeout:这是一个超时间,当某些操作达到这个时间时,Master和Slave双方都会认为对方已经断开连接。实际上您可以将这个时间看成是一个租约到期的时间。那么这个操作时间会影响哪些操作呢?A、向Slave进行的数据同步操作本身不能超过这个时间;B、Slave向Master发送一个PING指令并等待响应的时间;C、Master向Slave发送PONG回复并等待ACK的时间。repldisabletcpnodelay:这个选项的默认值为no,它对优化主从复制时使用的网络资源非常有用。要明白这个参数的含义,就首先要解释一下tcpnodelay是个什么玩意儿?TCP数据报的报文头包含很多属性,这些属性基本上起到记录和保证传输目的、传输状态的作用,但没有数据报的所携带的业务数据(称之为有效载荷)。那么很明显,20个字节内容的信息分成20个数据报进行传输和只用一个数据报进行传输,需要占用的网络资源就完全不一样。JohnNagle在1984年发明了一种减轻网络传输压力的算法,就是为了解决这个问题(算法的名字就叫做Nagle,后续的技术人员又做了很多改进和升级)。其基本思路就是将要发送的内容凑够一定的数量后,再用一个数据报发送出去。如果该属性设置为yes,Redis将使用Nagle算法(或类似算法),让数据报中的有效载荷凑够一定数量后,在发送出去;设置成no,Redis就不会这么做。replbacklogsize:上文已经介绍过了Redis中为了进行增量同步所准备的环形内存区域,以及Redis这样做的原因额,所以这里就不再赘述了。这个选项就是用来设置环形内存的大小的,这个选项的默认值为1MB;正式的生产环境下可以稍微加大一些,例如5MB。slavepriority:当前Slave节点的优先级权重。我们后文会介绍一款Redis自带的监控和故障转移工具:RedisSentinel,这个工具允许一个Master节点下有多个Slave节点,并且可以自动切换Slave节点为Master节点。如果Slave节点的优先级权重值越低,就会再切换时有限成为新的Master节点。minslavestowrite和minslavesmaxlag:为了尽可能避免Master节点对应的多个Slave节点在数据复制过程中数据差异被越拉越大。Redis服务提供了一组拒绝数据写操作的策略,这个策略可以解释为:当Master上在minslavesmaxlag时间(单位秒)间隔后,任然有minslavestowrite个Slave和它正常连接,那么Master才允许进行数据写操作。ul 23、Master和Slave设置实例 讨论了Redis中主从复制的基本原理和Redis主配置文件中针对主从复制的设定选项意义后,我们来看一个实际设置过程。注意,由于这个过程非常简单所以我们会非常快。首先Master服务器不需要针对主从复制做任何的设置(这不包括对主从复制过程的配置优化)。所以我们就直接来看Slave节点的配置: Slave节点上我们只需要做一件事情,就是打开slaveof选项:。。。。。。slaveof选项的设置,给定master节点的ip和port就可以了192。168。61。140就是master节点slaveof192。168。61。1406379。。。。。。 接着,我们马上就可以看看同步效果了。首先确保您的master节点使工作正常的,然后就可以启动Slave节点了:。。。。。。5349:S17Dec04:20:00。773ConnectingtoMASTER192。168。61。140:63795349:S17Dec04:20:00。773MASTERSLAVEsyncstarted5349:S17Dec04:20:00。774NonblockingconnectforSYNCfiredtheevent。5349:S17Dec04:20:00。775MasterrepliedtoPING,replicationcancontinue。。。5349:S17Dec04:20:00。776Partialresynchronizationnotpossible(nocachedmaster)5349:S17Dec04:20:00。782Fullresyncfrommaster:976f0b31cbf6acd4fcc888301ea4639a7c591136:15349:S17Dec04:20:00。864MASTERSLAVEsync:receiving119bytesfrommaster5349:S17Dec04:20:00。865MASTERSLAVEsync:Flushingolddata5349:S17Dec04:20:00。865MASTERSLAVEsync:LoadingDBinmemory5349:S17Dec04:20:00。865MASTERSLAVEsync:Finishedwithsuccess5349:S17Dec04:20:01。068Backgroundappendonlyfilerewritingstartedbypid53525349:S17Dec04:20:01。082AOFrewritechildaskstostopsendingdiffs。5352:C17Dec04:20:01。082Parentagreedtostopsendingdiffs。FinalizingAOF。。。5352:C17Dec04:20:01。082Concatenating0。00MBofAOFdiffreceivedfromparent。5352:C17Dec04:20:01。082SYNCappendonlyfilerewriteperformed5352:C17Dec04:20:01。082AOFrewrite:6MBofmemoryusedbycopyonwrite5349:S17Dec04:20:01。168BackgroundAOFrewriteterminatedwithsuccess5349:S17Dec04:20:01。168ResidualparentdiffsuccessfullyflushedtotherewrittenAOF(0。00MB)5349:S17Dec04:20:01。168BackgroundAOFrewritefinishedsuccessfully。。。。。。 笔者在Slave节点上开启了定期的RDB快照和AOF日志功能,所以各位读者可以忽略那些日志信息,直接关注ConnectingtoMASTER。和MASTERSLAVE。这些日志信息就好。 以下是Master节点上给出的日志信息。。。。。。5614:M17Dec04:20:00。789Slave192。168。61。145:6379asksforsynchronization5614:M17Dec04:20:00。789Fullresyncrequestedbyslave192。168。61。145:63795614:M17Dec04:20:00。789StartingBGSAVEforSYNCwithtarget:disk5614:M17Dec04:20:00。791Backgroundsavingstartedbypid56205620:C17Dec04:20:00。814DBsavedondisk5620:C17Dec04:20:00。815RDB:6MBofmemoryusedbycopyonwrite5614:M17Dec04:20:00。875Backgroundsavingterminatedwithsuccess5614:M17Dec04:20:00。877Synchronizationwithslave192。168。61。145:6379succeeded。。。。。。 看来Master节点收到了Slave节点的连接信息,并完成了全量数据同步操作。 24、关闭RDB功能的说明 以上介绍的Master节点和Slave节点的设置是否特别简单?是的,实际上只需要打开了Slave节点上REPLICATION区域的slaveof选项就可以让Redis的主从复制功能运作起来。现在我们往回倒,回到上一篇文章的介绍。在上一篇文章介绍RDB快照功能的配置项时,文章提到了可以用以下方式关闭RDB快照功能:以下为默认的设置为,注释掉即可save9001save30010save6010000在设置以下选项,就可以关闭RDB功能save 但是根据本文对Redis主从复制的介绍,我们可以发现Redis的RDB快照功能实际上是无法真正关闭的!以上所谓关闭RDB功能的设置,只是关闭了Redis服务在正常工作时定期快照的条件设定,但只要有Slave节点请求全量数据同步,Master节点就会强制做一次RDB快照。并且如果客户端主动发送BGSAVE命令,要求Redis服务进行RDB快照时,Redis也会被动执行RDB快照操作。 但是本文还是建议在组建Redis高可用集群时,关闭Master节点上的RDB功能。读者一定要清楚这样做的原因:这不是为了像个别网络资料说的那样真正关闭Redis的RDB快照功能,而是尽可能减少Master上主动进行RDB操作的次数,并将RDB快照工作转移到各个Slave节点完成。 3、RedisSentinel Redis服务提供了性能较高的主从复制功能,但是没有提供原生的MasterSlave的切换功能。也就是说如果您只是配置了Redis的主从复制功能,那么在Master节点出现故障时,您必须手动将一台Slave状态的节点切换为Master状态。当然这个问题在RedisVersion2。8版本前是有标准解决方案的,那就是:KeepalivedRedis服务组成的高可用集群。 由Keepalived监控Redis高可用集群中Master节点的工作状态,并在异常情况下切换另一个节点接替工作。但是,这个方案是有一些问题了,其中之一就是所有的Slave节点在Standby状态时无法分担Master节点的任何性能压力即使您设置了readonly等参数也不行,因为VIP根本不会把请求切过去。并且这种方式还不太方便监控Redis高可用集群中各个服务节点的实时状态。 从Version2。8版本开始,Redis提供了一个原生的主从状态监控和切换的组件RedisSentinel。通过它技术人员不但可以完成Redis高可用集群的适时监控,还可以通过编程手段减轻集群中Master节点中读操作的压力。本节内容,我们向读者介绍这个RedisSentinel的简单使用。 31、基本配置 由于RedisSentinel是Redis原生支持的,以RedisVersion3。2为例,在下载安装后就可以直接使用命令redissentinel启动Sentinel了。Sentinel的主配置文件模板存放在Redis安装目录的下,默认名为sentinel。conf。以下命令可以启动Sentinel(启动Sentinel所依据的配置文件是一定要携带的参数):redissentinel。sentinel。conf RedisSentinel本身也支持集群部署,而且为了在生产环境下避免Sentinel单点故障,所以也建议同时部署多个Sentinel节点。部署多个Sentinel还有一个原因,就是提高MasterSlave切换的准确性。以下的配置文件介绍会说明这一点。 下面我们介绍一些Sentinel主配置文件中的关键配置,注意Sentinel主配置文件也有类似Redis主配置文件提供的访问保护模式(protectedmode)、访问者权限控制(authpass)等,但是它们的意义基本上类似前文介绍过的,在Redis主配置文件中的相似内容,所以这里就不再赘述了。 sentinelmonitor: 这个属性是RedisSentinel中的最主要设置元素,换句话说如果要开启Sentinel甚至可以只设置这个属性。它包括了四个参数:mastername,这个参数是一个英文名说明了Sentinel服务监听的Master节点的别名,如果一个Sentinel服务需要同时监控多个Master,这需要设置多个不同的ip和redisport,指向sentinel需要监控的Redis集群最初的那个Master节点(为什么会是最初呢?后文会说明)的ip和端口;quorum,投票数量这个参数很重要,如果是Sentinel集群方式下,它设定当quorum个Sentinel认为Master异常了,就判定该Master真的异常了。单个Sentinel节点认为Master下线了被称为主管下线,而quorum个Sentinel节点都认为Master下线的情况被称为客观下线。 sentinelparallelsyncs: 一旦原来的Master节点被认为客观下线了,Sentinel就会启动切换过程。大致来讲就是从当前所有Slave节点选择一个节点成为新的Master节点(这时在Redis中设定的slavepriority参数就会起作用了)。而其它的Slave其slaveof的Master信息将被sentinel切换到新的Master上。而一次同时并行切换多少个Slave到新的Master上就是这个参数决定的。如果整个Redis高可用集群的节点数量不多(没有超过6个),建议使用默认值就可以了。 主配置文件中被rewrite的参数内容:sentinel。conf文件中的配置内容会随着Sentinel的监控情况发生变化由Sentinel程序动态写入到文件中。例如sentinelknownslave参数、sentinelcurrentepoch参数和sentinelleaderepoch参数。 注意,在Sentinel中您只需要配置最初的Master的监控位置,无需配置Master下任何Slave的位置,Sentinel会自己识别到这些Master直接的或者间接的Slave。 32、切换效果 介绍完配置后,我们来简单看一个Sentinel工作和切换的例子。这个例子中的有一个Master节点和一个Slave节点,当Master节点出现故障时,通过Sentinel监控到异常情况并自动完成Slave状态的切换。 首先请保证您的Master节点和Slave节点都是正常工作的,这个过程可以参见笔者之前文章的介绍: 这里就不再赘述RedisMaster和RedisSlave的内容了,因为在本文第2节中已经详细介绍过。实际上您只需要打开Slave节点的主配置文件,并增加slaveof的配置信息,将其指向Master的IP和端口就可以了。以下是Sentinel节点主要更改的配置信息:。。。。。。sentinelmonitormymaster192。168。61。14063791。。。。。。 由于在测试环境中我们只使用了一个Sentinel节点,所以设置sentinelmonitor配置项中的quorum为1就可以了,代表有一个Sentinel节点认为Master不可用了,就开启故障转移过程。当然生产环境下不建议这样使用。 之后我们使用以下Sentinel的主要配置信息启动Sentinel:redissentinel。sentinel。conf。。。。。。8576:X19Dec00:49:01。085SentinelIDis5a5eb7b97de060e7ad5f6aa20475a40b3d9fd3e18576:X19Dec00:49:01。085monitormastermymaster192。168。61。1406379quorum1。。。。。。 之后主动终止原来Master的运行过程(您可以直接使用kill命令,或者拔掉网线,又索性直接关机),来观察Slave节点和Sentinel节点的日志情况: 当断开原来的Master节点后,Slave节点将提示连接失效并开始重试。当Sentinel开始进入故障转移并完成后,Salve又会打印相应的过程信息:。。。。。。8177:S19Dec00:53:17。467ConnectingtoMASTER192。168。61。140:63798177:S19Dec00:53:17。468MASTERSLAVEsyncstarted8177:S19Dec00:53:17。468ErrorconditiononsocketforSYNC:Connectionrefused。。。。。。8177:M19Dec00:53:18。134Discardingpreviouslycachedmasterstate。8177:M19Dec00:53:18。134MASTERMODEenabled(userrequestfromid3addr192。168。61。140:51827fd5namesentinel5a5eb7b9cmdage258idle0flagsxdb0sub0psub0multi3qbuf0qbuffree32768obl36oll0omem0eventsrcmdexec)8177:M19Dec00:53:18。138CONFIGREWRITEexecutedwithsuccess。。。。。。。 从以上Slave节点的内容可以看到,Slave被切换成了Master状态。那么Sentinel本身有哪些重要的日志信息呢?如下所示:。。。。。。当前Sentinel节点确定原Master主观下线8576:X19Dec00:53:18。074sdownmastermymaster192。168。61。1406379由于设置的quorum为1,所以一个Sentinel节点的主管下线就认为Master客观下线了8576:X19Dec00:53:18。074odownmastermymaster192。168。61。1406379quorum11第三代,每转移一次故障epoch的值1,不好意思,在书写测试实例前,本人已经自行测试了两次故障转移,所以这里看到的epoch为3这个信息会自动写入到Sentinel节点的主配置文件中8576:X19Dec00:53:18。074newepoch3开始进行故障转移8576:X19Dec00:53:18。074tryfailovermastermymaster192。168。61。1406379选举出主导故障转移的Sentinel节点,因为不是所有Sentinel节点都会主导这个过程8576:X19Dec00:53:18。084voteforleader5a5eb7b97de060e7ad5f6aa20475a40b3d9fd3e138576:X19Dec00:53:18。084electedleadermastermymaster192。168。61。14063798576:X19Dec00:53:18。084failoverstateselectslavemastermymaster192。168。61。1406379选择提升哪一个slave作为新的master8576:X19Dec00:53:18。156selectedslaveslave192。168。61。145:6379192。168。61。1456379mymaster192。168。61。14063798576:X19Dec00:53:18。156failoverstatesendslaveofnooneslave192。168。61。145:6379192。168。61。1456379mymaster192。168。61。14063798576:X19Dec00:53:18。211failoverstatewaitpromotionslave192。168。61。145:6379192。168。61。1456379mymaster192。168。61。1406379提升原来的slave8576:X19Dec00:53:19。201promotedslaveslave192。168。61。145:6379192。168。61。1456379mymaster192。168。61。1406379试图重写所有salves节点的配置信息,并让它们指向新的master8576:X19Dec00:53:19。201failoverstatereconfslavesmastermymaster192。168。61。1406379故障转移结束8576:X19Dec00:53:19。250failoverendmastermymaster192。168。61。1406379最终完成master节点的切换8576:X19Dec00:53:19。250switchmastermymaster192。168。61。1406379192。168。61。1456379注意原有的master节点会再显示一条作为主观下线,但是这次下线信息是以salve身份通知的这是因为这次故障切换后,原来的master就算再上线,也只会作为Slave节点了8576:X19Dec00:53:19。251slaveslave192。168。61。140:6379192。168。61。1406379mymaster192。168。61。14563798576:X19Dec00:53:49。305sdownslave192。168。61。140:6379192。168。61。1406379mymaster192。168。61。1456379。。。。。。 通过Slave节点和Sentinel节点的日志可以看到,在经过了短暂的时间后Sentinel成功将唯一一个Slave节点转换成了Master节点,并继续向外部提供服务。 之后我们重新启动原有Master节点,看看会发生什么:以下是原有Master启动后,在Sentinel显示的信息。。。。。。8576:X19Dec01:31:12。743rebootslave192。168。61。140:6379192。168。61。1406379mymaster192。168。61。14563798576:X19Dec01:31:12。805sdownslave192。168。61。140:6379192。168。61。1406379mymaster192。168。61。1456379。。。。。。 一个非常重要的现象是,当原来的Master节点再次启动时,即使配置文件中没有设定slaveof信息,它也会在Sentinel的协调下称为Slave节点。这是因为任何一次Master到Slave的切换都是要付出代价的,其中除了状态本身的判断外,还有Sentinel自身协调和选举过程(选举哪一个Sentinel进行实质的切换动作),还有新的Master的选定问题,甚至包括Slave的slaveof目标变化过程中需要处理的数据一致性问题等等工作。所以最好的办法就是:只要能够保证Redis高可用集群持续工作,就不进行Master状态的切换。 33、Java客户端配合Sentinel的使用 通过Sentinel组建的高可用集群对比通过第三方软件组建的高可用集群而言,有其明显的优点。例如可以实时返回集群中每个Redis节点的状态,且各节点间更能保持最佳的数据一致性,另外还可以在必要的时候通过转移客户端读操作,减轻Master节点的工作压力。但是它也有一个很明显的缺点,就是由于整个集群可以向调用者开放多个Redis节点的地址,且Sentinel本身并不能充当路由器的作用,所以当Redis高可用集群进行状态切换时,客户端可能并不清楚原有的Master节点已经失效了。如下图所示: 还好的是,Java最常用的Redis客户端jedis提供了一组针对Sentinel的集群工具,让客户端可以在获取当前Redis高可用集群中的Master节点后,再在这个Master节点上完成数据读写操作。但另外一个读操作的负载问题还是没有被解决,所有的读操作也只会在Master节点完成。 我们来看看一些关键代码:。。。。。。这是基本的连接配置当让这些属性都可以根据您的实际情况进行更改JedisPoolConfigpoolConfignewJedisPoolConfig();poolConfig。setMaxTotal(100);poolConfig。setMaxIdle(50);poolConfig。setMinIdle(20);poolConfig。setMaxWaitMillis(61000);poolConfig。setTestOnBorrow(true);以下是可用的多个Sentinel节点的ip和端口SetStringjedisClusterNodesnewHashSetString();jedisClusterNodes。add(192。168。61。140:26379);如果有多个Sentinel一个一个添加进去jedisClusterNodes。add(192。168。61。139:26379);JedisSentinelPooljedisSentinelPoolnewJedisSentinelPool(mymaster,jedisClusterNodes,poolConfig);开始插入信息for(Integerindex0;index10000;index){获取最新的master信息Jtry{masterjedisSentinelPool。getResource();}catch(JedisConnectionExceptione){如果出现异常,说明当前的maser断开了连接,那么等待一段时间后重试LOGGER。info(masterisloss,waitingfortryagain。。。。。。);synchronized(MasterSlaveApp。class){MasterSlaveApp。class。wait(5000);}}开始正式插入master。set((keyindex)。getBytes(),index。toString()。getBytes());LOGGER。info(write:keyindex);synchronized(MasterSlaveApp。class){停止0。5秒,以便观察现象MasterSlaveApp。class。wait(500);}}jedisSentinelPool。close(); 在示例代码中Sentinel节点只有一个,存在于192。168。61。140:26379上。如果是生产环境建议不要对Sentinel进行单点部署,否则一旦Sentinel单点崩溃会造成整个Redis高可用集群在客户端无法进行Master节点的切换。在初始阶段192。168。61。140:6379是master节点,然后我们在程序执行过程中将原有的master节点关闭,这时上面的客户端代码片段可能的输出以下日志信息(部分):。。。。。。14639〔main〕INFOredistest。test。MasterSlaveAppwrite:key2916144〔main〕INFOredistest。test。MasterSlaveAppmasterisloss,waitingfortryagain。。。。。。22148〔main〕INFOredistest。test。MasterSlaveAppmasterisloss,waitingfortryagain。。。。。。28151〔main〕INFOredistest。test。MasterSlaveAppmasterisloss,waitingfortryagain。。。。。。34155〔main〕INFOredistest。test。MasterSlaveAppmasterisloss,waitingfortryagain。。。。。。40159〔main〕INFOredistest。test。MasterSlaveAppmasterisloss,waitingfortryagain。。。。。。十二月20,20164:12:22下午redis。clients。jedis。JedisSentinelPoolinitPool信息:CreatedJedisPooltomasterat192。168。61。145:637946163〔main〕INFOredistest。test。MasterSlaveAppmasterisloss,waitingfortryagain。。。。。。51166〔main〕INFOredistest。test。MasterSlaveAppwrite:key3051670〔main〕INFOredistest。test。MasterSlaveAppwrite:key31。。。。。。 四、Redis集群方案:高性能 1、概述 从这篇文章开始我们一起来讨论Redis中两种高性能集群方案,并且在讨论过程中将上一篇文章介绍的高可用集群方案结合进去。这两种高性能集群方案是:Twemproxy和Redis自带的Cluster方案。 2、Redis高性能集群:Twemproxy 21、Twemproxy概要 Twemproxy是一个Twitter开源的一个RedisMemcache代理服务器,最早也是Twitter在使用。在Twitter决定开发Twemproxy时,互联网领域使用最广泛的缓存技术还是Memcache,那个时候Redis并没有提供原生的Cluster功能,甚至没有Bate版本。而Twemproxy(也称为nutcraker)恰恰是为了解决将多个独立的Redis节点组成集群,同时提供缓存服务的问题。 您可以在GitHub上下载Twemproxy(https:github。comtwittertwemproxy)。这个地址也可以算作Twemproxy的官网了,其上对Twemproxy的特性进行了简要描述,包括:轻量级的快速访问代理、减少客户端对Redis服务的直连、支持多个Redis节点同时工作、支持数据分片、支持多种Hash算法、故障检查和故障节点自动排除、支持多种操作系统Linux,BSD,OSX以及Solaris。 但是官网上并没有明确介绍Twemproxy的缺点,而且细心的朋友可以观察到官网上的源代码已经有相当时间没有更新了,这能说明什么问题呢?本文后续内容将进行说明。下图说明了Twemproxy的功能定位: 22、Twemproxy基本配置 Twemproxy的基本配置非常简单,直接解压安装后就可以运行。它的主要配置文件在conf目录下的nutcracker。yml文件。由于Twemproxy的安装太简单了,本文就不再进行描述了,这里给出安装命令就可以了(CentOS6。X7。X):先安装automake、libtool等第三方支持组件yuminstallyautomakelibtool然后解压下载的Twemproxy压缩包进入解压的目录后运行(生成configure)autoreconffvi正式开始安装。configureprefix您的安装目录 安装后打开nutcracker。yml文件,这个文件实际上已经有内容了,相当于一个各种运行场景下的配置示例。各位读者可以将这些配置信息注释掉或者直接删除。这里我们给出配置文件中一些关键的配置属性,大家只需要知道这些配置属性的意义,就可以进行灵活操作了: listen:这个参数用于设置Twemproxy监控的IP和端口信息,如果要监控本机的所有IP设备,则设置为0。0。0。0,例如0。0。0。0:22122。 hash:这个参数非常重要所以多说几句。Twemproxy可以使用一致性Hash算法,通过计算Key的Hash值定位这个Key对应的数据存储在下层Redis节点的哪个位置。注意不是计算Key的数据结构,而是Key对应数据的存储位置。Twemproxy支持多种Hash算法,包括:md5、crc16、fnv164、fnv1a64、fnv132、fnv1a32、hsieh、murmur、jenkins等。之前我们介绍过,考虑采用哪种Hash算法有两个重要指标:Hash算法的速度和Hash算法的碰撞率。例如之前号称破解了MD5算法的王小云教授就是依靠Hash碰撞完成的,但实际上这种方式算不算完全破解呢?行业内就有很多种观点了,这里笔者经验有限就不展开讨论了。但MD5算法的两个事实依然是存在的:MD5任然是不可逆的,同时现成的破解站点也是存在的:http:www。ttmd5。com。另外两个事实是MD5算法的碰撞率确实是各种Hash算法中碰撞率非常低的,但它确实不是所有Hash算法中速度最快的。显然,这里我们更看重的Hash值的计算速度而不是碰撞率,产生Hash碰撞的两个Key其后果无非是被分配到同一个Redis节点进行存储而已。所以这里我们推荐设置两种Hash算法,murmur和fnv1a64。hashtag:为了避免在业务级别有关联的数据因为Key的Hash值不同而被散落在不用的Redis服务上(原因是当存储部分相关联数据的Redis下线,这个完整的业务数据就会受到影响),Redis提供了一个设置参数hashtag来框定一个Key的部分字符串,并对它进行Hash计算。这样就可以保证有关联的业务数据在进行Hash计算时得到同一个计算结果,从而被分配到一个Redis节点进行存储。hashtag由两个字符组成,举个例子,设置hashtag为〔〕,这时客户端通过Twemproxy存储两个Key:user〔yinwenjie〕、sex〔yinwenjie〕,那么当Twemproxy计算这两个Key的Hash值时,就只会采用〔〕中的字符串yinwenjie进行,所以计算出来的Hash值都是一样的。最终这两个Key都会落到同一个Redis节点上进行存储。distribution:依据Key的数据分配模式。Twemproxy本身并不存储数据,它的一个重要功能就是依据客户端传来的Key,对存储数据的真实Redis节点进行定位。定位方式包括三种:ketama,一致性Hash算法,关于一致性Hash算法的介绍可以参考这篇文章:http:blog。csdn。netyinwenjiearticledetails46620711t1。modula,这种数据分配模式,是根据Key的Hash值取模,模的数量就是下层可工作的Redis节点数量。random,完全随机分配Key对应的真实Redis节点。timeout:这是一个超时时间,用来指定等待和Redis建立连接的超时时间,以及从Redis收到响应的超时时间。backlog:TheTCPbacklogargument。Defaultsto512。这是官网上的解释,很简单不是吗?实际上我们介绍Redis时也出现过类似的参数,这个参数允许当前同时进行连接的有效TCP连接数量,但是请配合Linux系统下的somaxconn的设置进行使用,否则它会失效。preconnect:这个参数的默认值为false,主要指代当Twemproxy服务启动时,是否需要预连接到下层的Redis服务上。redis、redisauth和redisdb:Twemproxy可以作为Redis和Memcache两种缓存服务的代理,当redis参数设置为false时代表它将作为Memcache的代理。另外如果下层的Redis设置了权限验证信息,则Twemproxy还要通过redisauth配置项进行相应的设置。最后,由于Redis支持多个数据库,那么Twemproxy默认情况下将提供编号为0的数据库的代理,如果要改变请通过redisdb参数进行设置。serverconnections:这个参数设置Twemproxy可以在每一个下层RedisMemcache服务上同时使用的连接数量,默认的值为1。autoejecthosts、serverfailurelimit和serverretrytimeout:Twemproxy支持自动下线(不再代理)失败的Redis服务,如果要打开这个功能,请设置autoejecthosts参数为true。这时,Twemproxy会在重试serverfailurelimit次数后将还没有连接测试成功的Redis服务从自身代理列表上去掉。而serverretrytimeout设置了每一次测试连接的等待超时时间。servers:这个参数是一个列表,列出了Twemproxy代理的Redis的IP地址、访问端口和权重。 以下展示了一个完整的可以使用的Twemproxy代理的配置文件:beta:listen:0。0。0。0:22122hash:fnv1a64hashtag:{}distribution:ketamaautoejecthosts:falsetimeout:400redis:trueservers:192。168。61。140:6379:1server1192。168。61。145:6379:1server2原有配置文件中的其它配置信息如果不使用则可以注释掉 以上配置信息中的各个属性已经在前文详细介绍,这里就不再赘述了。以下是Twemproxy的启动指令,记得要首先设置Linux下的环境变量:nutcrackerc。nutcracker。yml您还可以通过以下命令测试配置文件的正确性nutcrackerc。nutcracker。ymlt还有更多参数可选Usage:nutcracker〔?hVdDt〕〔vverbositylevel〕〔ooutputfile〕〔cconffile〕〔sstatsport〕〔astatsaddr〕〔istatsinterval〕〔ppidfile〕〔mmbufsize〕关于这些参数更详细的使用说明,可以参考官方文档中的说明 23、LVSTwemproxyKeepalivedRedisRedisSentinelSentinelAgent 在生产环境下搭建Redis高性能集群,如果其中只使用一个Twemproxy节点,那肯定是不合理的。因为那样做会存在Twemproxy单节点故障问题,所以至少应该使用两个Twemproxy节点。又因为Twemproxy服务的工作相对独立,为了增加访问性能可以使用两个甚至多个Twemproxy节点同时提供服务,其上统一使用LVS服务进行负载分发。根据这样的描述,我们可以构建一种在生产环境下使用的Redis高性能集群方案: 上图中我们使用了两组Twemproxy节点,每一组都有两个Twemproxy节点在同一时间分别处于Active状态和Standby状态,在使用Keepalived组件进行状态监控和浮动IP切换。这四个Twemproxy节点的配合信息完全一样,保证了无论数据读写请求通过LVS到达哪一个Twemproxy节点,最终计算出来的目标Redis节点都是一样的。 但是以上方案还是有问题,就是单个Redis节点的高可用性无法保证。虽然在这样的Redis集群中,每一个活动的Redis节点在宕机后都可以被Twemproxy自动下线,造成的数据丢失情况也因为使用了一致性Hash算法而被限制到了一个可控制的范围。但是毕竟会丢失一部分数据,而且丢失的数据规模会和集群中Redis节点数量成反比关系。所以我们还需要在上一个集群方案的设计上再进行调整,加入我们在上一篇文章中介绍的Redis主从同步方案和Sentinel监控功能,形成第二种方案。 Twemproxy提供了一个配合使用的扩展组件:RedisTwemproxyAgent,它的作用是监控Sentinel中Master节点的情况,并且将最新的Master节点情况通知Twemproxy。这样一来当下层某组Redis高可用集群发生MasterSlave状态切换时,Twemproxy就会适时对其下层代理配置情况作出调整。 另外,上图中给出的第二种生产环境下的Redis集群方案,一共有5组独立运行的Redis高可用集群组,每组Redis高可用集群都有一个Master节点和至少一个Slave节点,它们之间使用Redis原生提供的数据复制功能保持数据同步。最后这些Redis高可用集群组通过一组Sentinel进行状态监控,而这组Sentinel也是同时拥有一个Master节点和两个Slave节点的高可用集群。 3、Redis高性能集群:RedisCluster 31、Twemproxy的生产环境问题 可维护性上的问题: LVSTwemproxyKeepalivedRedisSentinelSentinelAgent的架构方案应该是笔者迄今为止介绍的层次最多,且每层组件最多的单一系统架构。Keepalived在LVS和Twemproxy都有使用,所以在不将Keepalived单独算作一层的情况下就是4层结构(这里说的层次都限于指本公司机构的运维团队需要进行维护的系统组件)。而我们介绍过的Nginx集群方案是两层架构(LVSNginx),由于智能DNS路由一般是购买所以不参与计算;介绍过的ActiveMQ生产集群可以是三层架构(ZookeeperActiveMQLevelDB),也可以是两层架构(ActiveMQKahaBD关系型数据库);介绍过的MySQL分库分表集群是三层架构(LVSMyCATMySQL节点)。架构层次越多、每一层使用的组件越多,给运维团队带来的维护压力就越大,给生产环境带来的不稳定因素也越大。很显然从运维角度出发,为了解决单一功能而使用四层架构的情况是不太多见的除非业务功能不能改变且系统架构层面又没有替代方案。 执行性能和设计思路问题: Twemproxy并不是目前执行速度最快的RedisProxy产品,例如豌豆荚在2014年开源的一款产品Codis就可以当做Twemproxy的替代方案。Codis对下层Redis节点的组织方式个人认为要优于Twemproxy,例如它将下层的Redis节点明确分为多个组,每个组中有一个Master和至少一个Slave节点,并且采用了类似随后要介绍的RedisCluster那样的预分片方式(Slot),另外它还采用了ZK对各节点的工作状态进行协调。要知道Twemproxy虽然支持健康检查,也支持宕机节点的自动删除,但是Twemproxy并不支持数据转移。也就是说当某个Redis节点下线后,其上的数据也不会转移到其它节点上,而且Twemproxy中使用一致性Hash算法的基点或者取模运算所使用的基数也会发生变化。而如果引入的组的概念后,就可以减轻这个问题产生的风险,因为在一个组中的Master节点一旦出现问题,就会有Slave节点来接替它,而不会出现数据丢失问题。最后,根据豌豆荚自己的测试和广大网友自行测试的结果看,Codis对下层Redis节点的代理性能也要优于Twemproxy。 其它问题: Redis的数据结构中,我们可以使用Set结构进行交并补运算。但是Twemproxy代理不支持这样的运算。另外Twemproxy也不对事务功能提供支持。 32、Redis原生的Cluster支持 可以说Twemproxy是早期Redis原生的Cluster没有成熟时的替代方案,而后Redis官方推荐的高性能集群方案还是基于其原生的RedisCluster功能。RedisCluster从Redis3。0开始引入,实际上那个时候还是一个Bate版本,光放也不建议在生产环境下使用。但是到了目前最新的Version3。2版本,RedisCluster已经非常稳定了。 33、RedisCluster基本配置示例 这里我们给出一个RedisCluster的安装示例,首先介绍一下这个RedisCluster的配置示例要达到的部署效果,这样才便于各位读者继续阅读。在这个示例场景中我们有两台物理机,每台物理机上启动了三个Redis节点,一共六个节点,并让它们按照Cluster模式工作起来。如下表所示: 请注意,在生产环境中笔者并不建议在一台物理机上虚拟机上部署多个Redis节点,因为这样大大增加了多个Redis节点同时不可用的风险,但这是示例场景所以无所谓啦。 331、Redis安装和配置文件部署 由于有六个节点参与到集群中,所以我们需要准备六份不同的配置文件。读者可以将这6个文件存放到不同的文件夹下:192。168。61。145:6379NETWORKbind192。168。61。145port6379GENERALpidfilevarrunredis6379。pidREDISCLUSTERclusterenabledyesclusterconfigfilenodes。1456379clusternodetimeout15000APPENDONLYMODEappendonlyyes192。168。61。145:6380NETWORKbind192。168。61。145port6380GENERALpidfilevarrunredis6380。pidREDISCLUSTERclusterenabledyesclusterconfigfilenodes。1456380clusternodetimeout15000APPENDONLYMODEappendonlyyes192。168。61。145:6381NETWORKbind192。168。61。145port6381GENERALpidfilevarrunredis6381。pidREDISCLUSTERclusterenabledyesclusterconfigfilenodes。1456381clusternodetimeout15000APPENDONLYMODEappendonlyyes192。168。61。140:6379NETWORKbind192。168。61。140port6379GENERALpidfilevarrunredis6379。pidREDISCLUSTERclusterenabledyesclusterconfigfilenodes。1406379clusternodetimeout15000APPENDONLYMODEappendonlyyes192。168。61。140:6380NETWORKbind192。168。61。140port6380GENERALpidfilevarrunredis6380。pidREDISCLUSTERclusterenabledyesclusterconfigfilenodes。1406380clusternodetimeout15000APPENDONLYMODEappendonlyyes192。168。61。140:6381NETWORKbind192。168。61。140port6381GENERALpidfilevarrunredis6381。pidREDISCLUSTERclusterenabledyesclusterconfigfilenodes。1406381clusternodetimeout15000APPENDONLYMODEappendonlyyes 以上只是列举了要参与RedisCluster的六个节点中和本节内容相关的重点配置项,包括网络配置、一般性配置和集群部分的配置。其它的配置项可以根据读者所处技术环境的自行决定,例如是否开启主动SNAPSHOTTING的策略问题,因为Cluster中的Master都会有一个或者多个Slave节点,所以基本上一组高可用集群的数据不会同时丢失,而Master和Slave间的数据同步还是依靠Redis原生的主从同步方案完成的,所以RedisMaster节点还是会做被动作SNAPSHOTTING动作。以下是六个节点的启动命令:启动145上的三个redis节点redisserver。redis。conf。1456379redisserver。redis。conf。1456380redisserver。redis。conf。1456381启动140上的三个redis节点redisserver。redis。conf。1406379redisserver。redis。conf。1406380redisserver。redis。conf。1406381 以上启动命令和您放置配置文件具体位置有关、和您是否设定了环境变量有关,还和您准备如何查看命令执行日志有关。所以具体执行参数肯定是有差异的。请注意,在第一次单独启动某个Redis节点时,您可能会看到类似以下的提示:redis。conf。1456380节点。。。。。。18449:M29Dec18:32:52。036Ihavekeysforunassignedslot95。Takingresponsibilityforit。18449:M29Dec18:32:52。036Ihavekeysforunassignedslot219。Takingresponsibilityforit。18449:M29Dec18:32:52。038Ihavekeysforunassignedslot641。Takingresponsibilityforit。。。。。。。redis。conf。1456381节点。。。。。。9582:M29Dec18:43:49。048Ihavekeysforunassignedslot95。Takingresponsibilityforit。9582:M29Dec18:43:49。048Ihavekeysforunassignedslot219。Takingresponsibilityforit。9582:M29Dec18:43:49。048Ihavekeysforunassignedslot641。Takingresponsibilityforit。。。。。。。 这是因为Redis启动时,会自动创建技术人员在clusterconfigfile配置项设定的集群配置文件,例如nodes。1406380、nodes。1406381这些文件,并且会默认托管一些slots。但细心的读者可以发现,这六个节点独立启动时默认托管的slots信息都是一样的。这是因为这些节点还没有建立通讯机制,并不能协调slot的管理信息。而且这些clusterconfigfile中都会默认自身节点是一个Master节点。 经过以上过程我们启动了六个节点,但是到目前为止这六个节点还是独立工作的并没有形成集群。这是因为各个clusterconfigfile中并没有明确协调哪些节点将成为Master节点,哪些节点将成为Slave节点并且他们的主从映射关系,也没有协调任何和节点发现有关的信息,同样也没有协调各个节点的ID信息或者节点所映射的Master的ID信息,更没有协调各个节点分别负责的slot信息。那么以上这些协调动作都是通过下一个操作步骤。 (上图说明了各个Redis节点单独启动后的RedisCluster状态) 332、Ruby安装和集群命令运行 RedisCluster通过运行一个Ruby脚本进行初始化和启动,如果您的操作系统还没有安装Ruby,请进行安装(以下示例的安装命令适用于CentOS):yuminstallyrubyrubygems。。。。。。geminstallredisSuccessfullyinstalledredis3。3。21geminstalledInstallingridocumentationforredis3。3。2。。。InstallingRDocdocumentationforredis3。3。2。。。。。。。。。 在Redis的源文件目录的src目录中,有一个Ruby脚本文件redistrib。rb,通过运行这个脚本文件可以完成RedisCluster的初始化和启动操作。如果各位读者希望以后都能方便的运行这个脚本文件,可以先将这个脚本文件Copy到Redis的执行目录下:cp你的源码路径redistrib。rbusrlocalbinredistrib。rb或者cp你的源码路径redistrib。rbusrredisbinredistrib。rb。。。。。。 接下来就可以运行这个脚本了:redistrib。rbcreatereplicas1192。168。61。140:6379192。168。61。140:6380192。168。61。140:6381192。168。61。145:6379192。168。61。145:6380192。168。61。145:6381CreatingclusterPerforminghashslotsallocationon6nodes。。。Using3masters:192。168。61。145:6379192。168。61。140:6379192。168。61。145:6380Addingreplica192。168。61。140:6380to192。168。61。145:6379Addingreplica192。168。61。145:6381to192。168。61。140:6379Addingreplica192。168。61。140:6381to192。168。61。145:6380M:1cf10fb6d7c0ad4d936b1c061a99d370bda07757192。168。61。140:6379slots:546110922(5462slots)masterS:8749db7b6a5860be63f592e94388239a7467cbb1192。168。61。140:6380replicates3ee2a9f173ccbee3a5a79b082af2910be7d22e57S:33f9ee49963a32220984122278105cdda7761517192。168。61。140:6381replicates120bc340ed1b24ba8e07368cf18d433094644e6eM:3ee2a9f173ccbee3a5a79b082af2910be7d22e57192。168。61。145:6379slots:05460(5461slots)masterM:120bc340ed1b24ba8e07368cf18d433094644e6e192。168。61。145:6380slots:1092316383(5461slots)masterS:0b107150f7c075fe7ba701b64a9f7bf9f7896ead192。168。61。145:6381replicates1cf10fb6d7c0ad4d936b1c061a99d370bda07757CanIsettheaboveconfiguration?(typeyestoaccept):yes 以上命令中,create参数代表创建一个新的新的RedisCluster,然后我们后给出了一个replicas参数,这个参数代表集群中的每一个Master节点对应多少个Slave节点,这里给出的数值是1,就代表每一个Master节点会对应一个Slave节点。需要注意,这里并不需要明确指定哪些节点将成为Master节点,哪些节点将成为Slave节点,而redistrib会参考replicas参数的值自行计算得出。在命令的最后我们还给出了参与这个新的RedisCluster的所有Redis节点的信息。 redistrib会根据以上这些参数预计一个可能的配置信息,特别是初始化的Master和Slave节点的预计情况、每个节点的ID编号以及每个Master节点负责的Slot。接着redistrib会将这份报告呈现给技术人员,由后者最终确定是否执行初始化。输入yes,redistrib就将按照这份计划执行RedisCluster的创建工作了:。。。。。。PerformingClusterCheck(usingnode192。168。61。140:6379)M:1cf10fb6d7c0ad4d936b1c061a99d370bda07757192。168。61。140:6379slots:546110922(5462slots)master1additionalreplica(s)S:33f9ee49963a32220984122278105cdda7761517192。168。61。140:6381slots:(0slots)slavereplicates120bc340ed1b24ba8e07368cf18d433094644e6eS:0b107150f7c075fe7ba701b64a9f7bf9f7896ead192。168。61。145:6381slots:(0slots)slavereplicates1cf10fb6d7c0ad4d936b1c061a99d370bda07757M:3ee2a9f173ccbee3a5a79b082af2910be7d22e57192。168。61。145:6379slots:05460(5461slots)master1additionalreplica(s)S:8749db7b6a5860be63f592e94388239a7467cbb1192。168。61。140:6380slots:(0slots)slavereplicates3ee2a9f173ccbee3a5a79b082af2910be7d22e57M:120bc340ed1b24ba8e07368cf18d433094644e6e192。168。61。145:6380slots:1092316383(5461slots)master1additionalreplica(s)〔OK〕Allnodesagreeaboutslotsconfiguration。Checkforopenslots。。。Checkslotscoverage。。。〔OK〕All16384slotscovered。 到此为止6个节点的RedisCluster就创建完成了,除了初始化创建RedisCluster外,您还可以参考官方网站上的介绍完成RedisCluster中的节点新增、删除或者其它操作:https:redis。iotopicsclustertutorial。 333、使用客户端进行连接 客户端进行集群环境的连接,就是一个更简单的工作了。实际上Redis的客户端并不需要连接到RedisCluster中的所有节点,就可以完整操作RedisCluster中的数据。这是因为每个RedisCluster中的节点都清楚整个集群的全局情况,特别是Slot存在的位置。以下示例代码展示了如何通过Java代码连接到RedisCluster:。。。。。。JedisPoolConfigconfignewJedisPoolConfig();config。setMaxTotal(10);config。setMaxIdle(2);这里添加集群节点。可以添加多个节点,但并不是需要添加Cluster的所有节点HostAndPortnode0newHostAndPort(192。168。61。140,6379);HostAndPortnode1newHostAndPort(192。168。61。145,6379);SetHostAndPortnodesnewHashSetHostAndPort();nodes。add(node0);nodes。add(node1);创建和连接到集群JedisClusterjedisClusternewJedisCluster(nodes,5000,10,config);做你要做的Redis操作吧,少年jedisCluster。close();。。。。。。 版权声明:本文为CSDN博主说好不能打脸的原创文章,遵循CC4。0BYSA版权协议,转载请附上原文出处链接及本声明。 原文链接:https:blog。csdn。netyinwenjiearticledetails53905637