范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

Redis技术详解

  一、Redis基本概念和安装使用
  1、综述
  Redis是一款内存数据库,所谓内存数据库是指它存储数据的主要介质是内存而非传统意义的磁盘,后者只用于辅助功能。Redis可以当作NoSQL数据库,缓存和消息代理来使用,目前各行业实践中使用Redis最多的场景还是把它当成缓存子系统,例如存储在线用户的登录情况,存储1小时内提交的订单情况等,缓存图片路径或者图片内容等等;其次较多的场景是作为消息代理来使用,例如DUBBO支持使用Redis进行事件订阅和通知。
  Redis的发起者是Salvatore Sanfilippo,最初开发它的目的就是为了解决快速存储和查询社交网站上常见的好友关系数据。目前Vmware在资助着Redis项目的开发和维护,Redis最新的版本是3.X(本文写作时为version 3.2.5),其中内置支持了很多实用的数据存储结构,例如string, hashes, lists, set, sorted sets等,还提供了很多实用的高性能、高可靠特性,例如集合运算、LRU缓存内容管理、主从同步等等。
  在本专题讨论Redis时,会首先花一些较少的篇幅介绍Redis的基本安装和使用,然后将深入Redis所支持的数据结构主要讲解Redis底层设计对这些数据结构的支撑,接下来会介绍Redis的主要配置优化事项,最后介绍Redis的集群搭建方式(基于3.X版本)和实施案例。
  2、快速安装和基本配置说明
  2-1、快速安装
  Redis官方网站非常好记:https://redis.io/,安装也非常简单。请到官网下载最新发布的稳定版本:http://download.redis.io/releases/redis-3.2.5.tar.gz,解压后可直接make & make install。注意,类似Centos 6.X的老版本操作系统,在安装时可能会报一些错误,依次解决就行了:
  make: cc: Command not found make:# make install > make: cc: Command not found make***
  遇到这个错误,说明没有安装编译工具。yum安装 gcc 即可:# yum install -y gcc
  You need tcl 8.5 or newer in order# make test > error:You need tcl 8.5 or newer in order
  这是因为相应软件的版本太老了,下载一个安装就行:# wget http://downloads.sourceforge.net/tcl/tcl8.6.1-src.tar.gz ...... 解压 ...... # cd ./unix/ ...... 安装即可 ......
  安装过程中还可能有其它问题,按照报错解决就可以了
  这些问题解决后,就可以安装Redis 3.2的版本了:# make install ...... Hint: It"s a good idea to run "make test" ;) ...... INSTALL install INSTALL install INSTALL install INSTALL install INSTALL install ......
  2-2、客户端连接测试
  Redis的客户端组件可以和Spring框架无缝集成,还兼容多种开发语言:如PHP、Go、C#、python、C++、NodeJS等等。下面我们单纯使用Redis的Java客户端组件Jedis,完成一个简单的Redis服务端连接,并向服务器写入一个Key-Value值。我们使用的Jedis版本为2.8,您首先需要在Maven工程中引入这个组件:......  redis.clients jedis 2.8.0  ......
  以下是Java测试程序的主要代码片段:...... Jedis redisClient = new Jedis("192.168.61.140", 6379); // 写入一个K-V值 redisClient.set("key1", "value"); redisClient.close(); ......
  通过以上代码,我们向Redis服务器写入了一个K-V键值对,那么这个操作是否成功了呢?我们可以使用Redis自带的Client功能进行查看:# redis-cli -h 192.168.61.140 192.168.61.140:6379> get key1 "value"
  Redis自带的Client功能非常强大,包括写入、查询、监控、调试等,从以上的"get"命令结果来看Java客户端代码上的写入操作是成功了。但那只是最简单的写入操作,Redis支持的多种复杂数据结构以及他们的组织方式,我们将在后文进行详细讨论。
  3、基本配置参数
  Redis 3.2版本中,最重要的配置文件就是存在于安装目录中的名叫redis.conf的文件。其中的范本格式清楚说明了Redis 3.2版本中可以配置的主要信息和含义。主要包括几个方面:网络配置项、一般配置项、快照配置项、复制(高可用)配置项、集群配置项、安全配置项、资源限制配置项、LUA脚本配置、慢日志配置、监控配置、事件通知配置、数据结构高级配置等。
  本节内容将首先讨论一些基础配置项,后续文章中在介绍Redis对各种数据结构支持时再介绍数据结构相应的配置项,介绍Redis数据快照原理时再介绍快照配置项,介绍Redis高可用方案和集群方案时再介绍相应的配置选项。
  3-1、网络配置项和一般配置项bind:绑定的主机地址,不设置默认将处理所有请求。由于一台主机可能有多个IP,如果这里设置192.168.1.100,那么Redis只会处理达到这个IP的数据请求。protected-mode:设置是否开启网络保护模式,默认为yes(开启)。这个选项的工作方式是,如果您没有设置了bind参数,也没有设置安全登录密码,则当这个参数开启时Redis只允许本地连接的客户端访问。也就是说如果您不设置bind参数,而又需要其它客户端通过网络访问,那么就设置这个参数为no(关闭)吧。port:Redis服务的工作端口,默认为6379。tcp-backlog:在Linux服务器上有一个配置参数"net.core.somaxconn",这是参数表示socket监听(listen)的backlog上限。所谓backlog是指已经和服务器完成了TCP三次握手确认,但是还没有被上层应用程序处理的请求队列。这个参数在操作系统层面的默认值是128,很显然在压力非常大的系统上,这个值小了一点。您可以修改成2048或者更大,而为Redis配置的tcp-backlog应该小于或者等于您在操作系统上设置的最大限制值(否则Redis上面设置再大也没有用)。net.core.somaxconn参数的调整可参见其它资料。timeout:该参数表示当某一个客户端连接上来并闲置timeout(单位秒)的时间后,Redis服务端就主动关闭这个客户端连接。该配置参数的默认值为0,表示关闭这个功能。tcp-keepalive:客户端TCP连接的健康性检查,如果不设置为0就表示Redis服务端会定时发送SO_KEEPALIVE心跳机制检测客户端的反馈情况。该配置的默认值为300秒,既是300秒检测一次。健康性检查的好处是,在客户端异常关闭的情况下,Redis服务端可以发现这个问题,并主动关闭对端通道。这个参数建议开启。daemonize:当为yes的时候,以守护进程的模式运行。该参数的默认值为 no,主要目的是为了在测试环境下调试方便;当运行在生产环境时,可以将这个选项配置为yes。pidfile:当Redis没有以守护进程的方式运行,这个pid文件的配置选项不会起作用。当redis以守护进程方式运行时,这个配置选项设置的文件就会被创建和使用。loglevel:配置的Redis日志级别,默认为notice。这个选项可以设置以下这些值:debug,非常多的信息无论有用的还是没用的都会被显示或者记录,一般用于开发和测试状态;verbose,比起debug要少很多,但是也极少出现有用的信息;notice,信息量适度而且一般不会错过有用的信息,可用于生产环境;warning,只有非常有用和危险的信息会被显示或者记录,建议使用warning级别的日志记录模式。logfile:日志文件存在的位置,默认为空字符串设置,这时日志会打印到标准输出设备。syslog-enabled:默认为no(开启),当配置为yes时,Redis的日志会输出到系统日志。databases:从Redis很早的版本开始,它就支持多个独立的内存数据库同时存在。该值设置数据库的数量,默认数据库为16,可以使用SELECT  命令在连接上指定数据库id。
  3-2、安全和资源限制配置项
  requirepass:该选项指定客户端在完成连接后,在执行任何其它命令前必须使用的鉴权密码。这在技术团队需要拒绝一些不信任的客户端访问Redis服务的情况下,是比较有用的。
  rename-command:在生产环境下,有一些Redis命令是非常危险的,例如FLUSHALL命令和FLUSHDB命令。所以Redis服务器为生产环境的指令安全性提供了一个重命名功能,通过这个功能我们可以将一些需要避免执行的指令变更成其它指令。例如以下设定可以将FLUSHALL指令变更成空字符:...... rename-command FLUSHALL "" rename-command FLUSHDB "" ......
  这是再使用Redis的客户端执行以上命令,就会收到以下的提示:...... // 开启了密码功能,并设置密码为123456 # redis-cli -h 192.168.61.140 -a 123456 192.168.61.140:6379> FLUSHDB (error) ERR unknown command "FLUSHDB" 192.168.61.140:6379> FLUSHALL (error) ERR unknown command "FLUSHALL" ......maxclients:设定当前同时连接到本Redis服务上的客户端数量,默认为10000。注意这个值的设定还和操作系统上file limit参数有关联,实际情况是10000个同时正常的客户端连接完全够用,如果实际情况需要调整则一定要同时确认该设定值没有超过操作系统上设定的文件描述符限制数量。maxmemory:该选项是告诉Redis当使用了多少物理内存后就开始拒绝后续的写入请求,该参数能很好的保护好你的Redis不会因为使用了过多的物理内存而导致swap,最终严重影响性能甚至崩溃。该参数的设置单位为bytes,建议的设置范围不要超过操作系统可支配内存的 3/5,例如您的操作系统可支配8GB内存,那么建议的设置值就是5GB,也就是5242880k、5120mb、5gb。在配置容量大小时,Redis支持各种单位简写,也就是说1GB 1Gb 1gB都是一个意思。如果不设置该参数就表示不限制,但这种方式在生产环境下不推荐。maxmemory-policy:这个参数非常重要,它设置了当内存达到最大可用数量时的内存清除策略。该选项可以设置以下这些值:volatile-lru设定值,使用Redis时如果不是有特殊的存储要求,都建议设定Key的过期时间expire。一旦设置了过期时间Redis会将这些Key放置到一个LRU队列中(关于LRU队列算法的内容可以参考本专题之前的文章),Redis使用"惰性删除"+"定期删除"的方式对LRU队列中过期的Key进行清理。当内存达到满载状态时,Redis也会启动一次清除操作。allkeys-lru设定值将忽略Key的过期时间设定,将所有Key放入LRU队列并清除过期的数据。volatile-random该选项将对过期的Key值采取"随即选取"算法进行清除。allkeys-random选项和volatile-random选项类似,只不过将Key的选取范围扩大到了所有Key,而不只针对设定了过期时间expire的Key。volatile-ttl,该选项将清除当前即将过期的Key(最小TTL算法)。noeviction,如果清除策略设置该值,在Redis内存满载情况下将不启动任何清除策略,直接拒绝后续的写操作。noeviction是一个默认策略,所以建议在生产环境中一定要进行清除策略的修改。maxmemory-samples:以上提到的LRU算法和TTL算法在Redis中的实现并不是固定的,Redis支持技术人员根据实际情况在算法性能和精度间进行调整。maxmemory-samples配置项实际上就是一个Key的抽样值,值越高算法精度越高。例如设置为10的时候,Redis中的LRU算法基本上处理失效的Key就非常精确了,但是却需要消耗较多的CPU资源;设置为3的时候,虽然处理失效的Key精度不高,但是处理速度非常快。该选项的默认值为5。
  3-3、日志和监控配置项
  3-3-1、慢日志
  Redis从很早的版本就提供了一个慢操作的日志记录功能,称为慢日志。当Redis中某一个内部操作所耗费的时间超过了设定的一个限制阀值,这个操作就会被慢日志功能记录在内存中。注意这里提到的是内部操作时间,并不包括可能的网络I/O操作时间。
  通过Redis的客户端使用slowlog get、slowlog len、slowlog reset等命令可以查看当然Redis服务端记录的慢日志情况。Redis的配置信息中提供了两个参数供技术团队调整慢日志功能:slowlog-log-slower-than和slowlog-max-len。
  slowlog-log-slower-than:这个参数就是给定慢日志的时间阀值,单位为微妙。也就是说1000000等于1秒。该参数的默认为10000。请注意,如果该参数的值设置为负数,将禁用慢日志功能;如果设置为0,将会记录每一条操作日志。
  slowlog-max-len:这个参数存储Redis服务端能够记录的慢日志总长度。这个数值没有上限,只是需要技术团队注意这会消耗宝贵的内存资源,实际上默认的128就是比较实用的设置值。
  3-3-2、延迟监控功能
  LATENCY监控器功能,是从Redis Version 2.8.3开始提供的一个重要的反映其延迟状态的监控功能。其工作任务是当技术人员设置了一个延迟操作的时间阀值后,凡是超过了这个阀值时间的操作都将被认为是执行缓慢的操作被记录下来。Redis客户端在登录后,可以使用诸如LATENCY LATEST、LATENCY HISTORY、LATENCY RESET、LATENCY GRAPH、LATENCY DOCTOR等命令控制/查询LATENCY监控器。LATENCY监控器功能更详细的介绍,可以参看Redis官方文档中的介绍:https://redis.io/topics/latency-monitor。
  latency-monitor-threshold,这个参数项用来设置操作时间的阀值,如果关闭延迟监控功能的话,可以将这个值设置为0。另外客户端连接可以独立设置这个阀值,只需要在连接Redis服务端成功后,执行"CONFIG SET latency-monitor-threshold XXXX"命令即可。
  二、Redis事件订阅和持久化存储
  3-4、事件功能和配置项
  Redis从2.X版本开始,就支持一种基于非持久化消息的、使用发布/订阅模式实现的事件通知机制。所谓基于非连接保持,是因为一旦消息订阅者由于各种异常情况而被迫断开连接,在其重新连接后,其离线期间的事件是无法被重新通知的(一些Redis资料中也称为即发即弃)。而其使用的发布/订阅模式,意味着其机制并不是由订阅者周期性的从Redis服务拉取事件通知,而是由Redis服务主动推送事件通知到符合条件的若干订阅者。
  Redis中的事件功能可以提供两种不同的功能。一类是基于Channel的消息事件,这一类消息和Redis中存储的Keys没有太多关联,也就是说即使不在Redis中存储任何Keys信息,这类消息事件也可以独立使用。另一类消息事件可以对(也可以不对)Redis中存储的Keys信息的变化事件进行通知,可以用来向订阅者通知Redis中符合订阅条件的Keys的各种事件。Redis服务的事件功能在实际场景中虽然使用得不多,不过还是可以找到案例,例如服务治理框架DUBBO默认情况下使用Zookeeper作为各节点的服务协调装置,但可以通过更改DUBBO的配置,将Zookeeper更换为Redis。
  3-4-1、publish和subscribe
  我们先从比较简单的publish命令和subscribe命令开始介绍,因为这组命令所涉及到的Channel(通道)和Redis中存储的数据相对独立。publish命令由发送者使用,负责向指定的Channel发送消息;subscribe命令由订阅者使用,负责从指定的一个或者多个Channel中获取消息。
  以下是publish命令和subscribe命令的使用示例:// 该命令向指定的channel名字发送一条消息(字符串) PUBLISH channel message // 例如:向名叫FM955的频道发送一条消息,消息信息为"hello!" PUBLISH FM955 "hello!" // 再例如:向名叫FM900的频道发送一条消息,消息信息为" doit!" PUBLISH FM900 "doit!" // 该命令可以开始向指定的一个或者多个channel订阅消息 SUBSCRIBE channel [channel ...] // 例如:向名叫FM955的频道订阅消息 SUBSCRIBE FM955 // 再例如:向名叫FM955、FM900的两个频道订阅消息 SUBSCRIBE FM955 FM900
  如果您使用需要使用publish命令和subscribe命令,您并不需要对Redis服务的配置信息做任何更改。以下示例将向读者展示两个命令的简单使用方式——前提是您的Redis服务已经启动好了:
  由客户端A充当订阅者,在ChannelA和ChannelB两个通道上订阅消息
  -- 我们使用的Redis服务地址为192.168.61.140,端口为默认值[root@kp2 ~]# redis-cli -h 192.168.61.140 192.168.61.140:6379> SUBSCRIBE ChannelA ChannelB Reading messages... (press Ctrl-C to quit) 1) "subscribe" 2) "ChannelA" 3) (integer) 1 1) "subscribe" 2) "ChannelB" 3) (integer) 2
  有客户端B从当订阅者,通过ChannelB发送消息给所有订阅者。
  -- 连接到Redis服务器后,直接运行PUBLISH命令,发送信息[root@kp1 ~]# redis-cli -h 192.168.61.140 192.168.61.140:6379> PUBLISH ChannelB "hello" (integer) 1
  以下是订阅者客户端A所受到的message信息:...... -- 这时订阅者收到消息如下: 1) "message" 2) "ChannelB" 3) "hello"
  从以上示例中可以看到,客户端A确实收到了客户端B所发送的消息信息,并且收到三行信息。这三行信息分别表示消息类型、消息通道和消息内容。注意,以上介绍的这组publish命令和subscribe命令的操作过程并没有对Redis服务中已存储的任何Keys信息产生影响。
  3-4-2、模式订阅psubscribe
  Redis中还支持一种模式订阅,它主要依靠psubscribe命令向技术人员提供订阅功能。模式订阅psubscribe最大的特点是,它除了可以通过Channel订阅消息以外,还可以配合配置命令来进行Keys信息变化的事件通知。
  模式订阅psubscribe的Channel订阅和subscribe命令类似,这里给出一个命令格式,就不再多做介绍了(可参考上文对subscribe命令的介绍):// 该命令可以开始向指定的一个或者多个channel订阅消息 // 具体使用示例可参见SUBSCRIBE命令 PSUBSCRIBE channel [channel ...]
  模式订阅psubscribe对Keys变化事件的支持分为两种类型:keyspace(键空间通知)和keyevent(键事件通知),这两类事件都是依靠Key的变化触发的,而关键的区别在于事件描述的焦点,举例说明:
  当Redis服务中0号数据库的MyKey键被删除时,键空间和键事件向模式订阅者分别发送的消息格式如下:// 以下命令可订阅键空间通知 // 订阅0号数据库任何Key信息的变化 192.168.61.140:6379> psubscribe __keyspace@0__:* Reading messages... (press Ctrl-C to quit) 1) "psubscribe" 2) "__keyspace@0__:*" 3) (integer) 1 // 出现以上信息,说明订阅成功 // 当其他客户端执行 set mykey 123456 时,该订阅可收到以下信息 1) "pmessage" 2) "__keyspace@0__:*" 3) "__keyspace@0__:mykey" 4) "set"
  以上收到的订阅信息,其描述可以概括为:"mykey的键空间发生了事件,事件为set"。这样的事件描述着重于key的名称,并且告诉客户端key的事件为set。我们再来看看订阅键事件通知时,发生同样事件所得到的订阅信息:// 以下命令可订阅键事件通知 // 订阅0号数据库任何事件的变化 192.168.61.140:6379> psubscribe __keyevent@0__:* Reading messages... (press Ctrl-C to quit) 1) "psubscribe" 2) "__keyevent@0__:*" 3) (integer) 1 // 出现以上信息,说明订阅成功 // 当其他客户端执行 set mykey 123456 时,该订阅可收到以下信息 1) "pmessage" 2) "__keyevent@0__:*" 3) "__keyevent@0__:set" 4) "mykey"
  以上收到的订阅信息中事件是主体,其信息可以概括为:"0号数据库发生了set事件,发生这个事件的key信息为mykey"。
  3-4-3、模式订阅的配置
  a、配置和通配符
  要使用psubscribe命令进行键事件的订阅,就首先需要在Redis的主配置文件中对模式订阅进行设定。注意,如果您只是使用psubscribe命令通过Channel发送消息到订阅者,或者更单纯的使用publish命令和subscribe命令组合通过Channel发送和接收消息,就不需要进行这样的配置。
  默认情况下Redis服务下的键空间通知和键事件通知都是关闭的。在redis.conf文件下,有专门的"EVENT NOTIFICATION"区域进行设定,设置的格式为:...... notify-keyspace-events [通配符] ......
  通配符的定义描述如下:K:启用keyspace键空间通知,客户端可以使用__keyspace@__为前缀的格式使用订阅功能。E:启用keyevent键事件通知,客户端可以使用__keyevent@__为前缀的格式使用订阅功能。g:监控一般性事件,包括但不限于对del,expire,rename事件的监控。$:启用对字符串格式(即一般K-V结构)命令的监控。l:启用对List数据结构命令的监控。s:启用对Set数据结构命令的监控。h:启用对Hash数据结构命令的监控。z:启用对ZSet数据结构命令的监控。x:启用对过期事件的监控。e:启用对驱逐事件的监控,当某个键因maxmemory达到设置时,使用策略进行内存清理,会产生这个事件。A:g$lshzxe通配符组合的别名,也就是说"AKE"这样的通配符组合,意味着所有事件。
  以下的几个实例说明了配置格式中通配符的用法:// 监控任何数据格式的所有事件,包括键空间通知和键事件通知 notify-keyspace-events "AKE" // 只监控字符串结构的所有事件,包括键空间通知和键事件通知 notify-keyspace-events "g$KExe" // 只监控所有键事件通知 notify-keyspace-events "AE" // 只监控Hash数据解构的键空间通知 notify-keyspace-events "ghKxe" // 只监控Set数据结构的键事件通知 notify-keyspace-events "gsExe"
  注意,在Redis主配置文件中进行事件通知的配置,其配置效果是全局化的。也就是说所有连接到Redis服务的客户端都会使用这样的Key事件通知逻辑。但如果单独需要为某一个客户端会话设置独立的Key事件通知逻辑,则可以在客户端成功连接Redis服务后,使用类似如下的命令进行设置:...... 192.168.61.140:6379> config set notify-keyspace-events KEA OK
  b、键事件订阅
  完成键事件的配置后,就可以使用psubscribe命令在客户端订阅消息通知了。这个过程还是需要使用通配符参数,才能完成订阅指定。通配符格式如下所示:psubscribe __[keyspace|keyevent]@__:[prefix] // 例如: // 订阅0号数据库中,所有的键变化事件,进行键空间通知 psubscribe __keyspace@0__:* // 订阅0号数据库,所有的键变化事件,进行键空间通知和键事件通知 psubscribe __key*@0__:*
  注意,就如上文所提到的那样,客户端能够进行键信息变化事件订阅的前提是Redis服务端或者这个客户端会话本身开启了相应配置。以下举例说明psubscribe命令中参数的使用方式:// 注意,Redis服务上的配置信息如下 // notify-keyspace-events "gsExe" // 即是说只允许监控Set结构的所有事件,并且之启用了键事件通知,没有启用键空间通知。 // 客户端使用以下命令开始订阅Key的变化事件 192.168.61.140:6379> psubscribe __key*@0__:* // 以上命令订阅了0号数据库所有键信息的变化通知,包括键事件通知和键空间通知 Reading messages... (press Ctrl-C to quit) 1) "psubscribe" 2) "__key*@0__:*" 3) (integer) 1 // 接着,已连接到Redis服务上的另一个客户端执行了如下命令 // > sadd mysetkey rt // 那么收到的消息通知为 1) "pmessage" 2) "__key*@0__:*" 3) "__keyevent@0__:sadd" 4) "mysetkey"
  以上实例操作中有两个问题需要单独进行说明:当客户端使用psubscribe命令进行订阅时(psubscribe __key*@0__:*),实际上是连同keyspace(键空间通知)和keyevent(键事件通知)一起订阅了。那么按照上文介绍的内容来说,这个订阅者本该收到两条事件消息。一条消息的描述重点在key上,另一条消息的描述重点在sadd事件上。但实际情况是,这个订阅者只收到了以描述重点在事件上的键事件通知。这是因为在以上实例中特别说明的一点:Redis服务端只开启键事件通知的配置。所以无论客户端如何订阅键空间通知,也收不到任何消息。另外,包括Redis官方资料在内的资料都在阐述这样一个事实,既是通过sadd命令对一个Set结构中的元素进行变更和直接通过"PUBLISH __keyevent@0__:sadd mysetkey"这样的命令向订阅者发送消息,在消息订阅者看来效果都是一样。但是这两种不同的操作过程对于Redis存储的Key数据,则是完全不一样的。前者的操作方式会改变Redis中存储的数据状况,但后者则不会。
  3-4-4、Redis订阅/发布功能的不足
  Redis提供的订阅/发布功能并不完美,更不能和ActiveMQ/RabbitMQ提供的订阅/发布功能相提并论。首先这些消息并没有持久化机制,属于即发即弃模式。也就是说它们不能像ActiveMQ中的消息那样保证持久化消息订阅者不会错过任何消息,无论这些消息订阅者是否随时在线。由于本来就是即发即弃的消息模式,所以Redis也不需要专门制定消息的备份和恢复机制。也是由于即发即弃的消息模式,所以Redis也没有必要专门对使用订阅/发布功能的客户端连接进行识别,用来明确该客户端连接的ID是否在之前已经连接过Redis服务了。ActiveMQ中保持持续通知的功能的前提,就是能够识别客户端连接ID的历史连接情况,以便确定哪些订阅消息这个客户端还没有处理。Redis当前版本有一个简单的事务机制,这个事务机制可以用于PUBLISH命令。但是完全没有ActiveMQ中对事务机制和ACK机制那么强的支持。而在我写作的"系统间通讯"专题中,专门讲到了ActiveMQ的ACK机制和事务机制。Redis也没有为发布者和订阅者准备保证消息性能的任何方案,例如在大量消息同时到达Redis服务是,如果消息订阅者来不及完成消费,就可能导致消息堆积。而ActiveMQ中有专门针对这种情况的慢消息机制。
  3-5、Redis持久化存储
  从严格意义上说,Redis服务提供四种持久化存储方案:RDB、AOF、虚拟内存(VM)和DISKSTORE。虚拟内存(VM)方式,从Redis Version 2.4开始就被官方明确表示不再建议使用,Version 3.2版本中更找不到关于虚拟内存(VM)的任何配置范例,Redis的主要作者Salvatore Sanfilippo还专门写了一篇论文,来反思Redis对虚拟内存(VM)存储技术的支持问题。
  至于DISKSTORE方式,是从Redis Version 2.8版本开始提出的一个存储设想,到目前为止Redis官方也没有在任何stable版本中明确建议使用这用方式。在Version 3.2版本中同样找不到对于这种存储方式的明确支持。从网络上能够收集到的各种资料来看,DISKSTORE方式和RDB方式还有这一些千丝万缕的联系,不过各位读者也知道,除了官方文档以外网络资料很多就是大抄。
  最关键的是目前官方文档上能够看到的Redis对持久化存储的支持明确的就只有两种方案(https://redis.io/topics/persistence):RDB和AOF。所以本文也只会具体介绍这两种持久化存储方案的工作特定和配置要点。
  3-5-1、RDB
  RDB中文名为快照/内存快照,它的过程很好理解,就是Redis按照一定的时间周期将目前服务中的所有数据全部写入到磁盘中。但这个过程说起简单,实际上呢有很多细节需要被处理。Redis主配置文件的"REPLICATION"部分,放置了对这个过程的配置选项。在我们后续文章中讲解Redis支持的主从复制时,也可以看到RDB的影子。
  上图反映了内存快照的大致过程,由于生产环境中我们为Redis开辟的内存区域都比较大(例如6GB),那么将内存中的数据同步到硬盘的过程可能就会持续比较长的时间,而实际情况是这段时间Redis服务一般都会收到数据写操作请求。那么如何保证数据一致性呢?RDB中的核心思路是Copy-on-Write,来保证在进行快照操作的这段时间,需要压缩写入磁盘上的数据在内存中不会发生变化。在正常的快照操作中,一方面Redis主进程会fork一个新的快照进程专门来做这个事情,这样保证了Redis服务不会停止对客户端包括写请求在内的任何响应。另一方面这段时间发生的数据变化会以副本的方式存放在另一个新的内存区域,待快照操作结束后才会同步到原来的内存区域。
  另一个问题是,在进行快照操作的这段时间,如果发生服务崩溃怎么办?很简单,在没有将数据全部写入到磁盘前,这次快照操作都不算成功。如果出现了服务崩溃的情况,将以上一次完整的RDB快照文件作为恢复内存数据的参考。也就是说,在快照操作过程中不能影响上一次的备份数据。Redis服务会在磁盘上创建一个临时文件进行数据操作,待操作成功后才会用这个临时文件替换掉上一次的备份。
  以下是Redis中关于内存快照的主要配置信息:快照周期:内存快照虽然可以通过技术人员手动执行SAVE或BGSAVE命令来进行,但生产环境下多数情况都会设置其周期性执行条件。Redis中默认的周期新设置如下:# 周期性执行条件的设置格式为 save   # 默认的设置为: save 900 1 save 300 10 save 60 10000 # 以下设置方式为关闭RDB快照功能 save ""
  以上三项默认信息设置代表的意义是:如果900秒内有1条Key信息发生变化,则进行快照;如果300秒内有10条Key信息发生变化,则进行快照;如果60秒内有10000条Key信息发生变化,则进行快照。读者可以按照这个规则,根据自己的实际请求压力进行设置调整。stop-writes-on-bgsave-error:上文提到的在快照进行过程中,主进程照样可以接受客户端的任何写操作的特性,是指在快照操作正常的情况下。如果快照操作出现异常(例如操作系统用户权限不够、磁盘空间写满等等)时,Redis就会禁止写操作。这个特性的主要目的是使运维人员在第一时间就发现Redis的运行错误,并进行解决。一些特定的场景下,您可能需要对这个特性进行配置,这时就可以调整这个参数项。该参数项默认情况下值为yes,如果要关闭这个特性,指定即使出现快照错误Redis一样允许写操作,则可以将该值更改为no。rdbcompression:该属性将在字符串类型的数据被快照到磁盘文件时,启用LZF压缩算法。Redis官方的建议是请保持该选项设置为yes,因为"it’s almost always a win"。rdbchecksum:从RDB快照功能的version 5 版本开始,一个64位的CRC冗余校验编码会被放置在RDB文件的末尾,以便对整个RDB文件的完整性进行验证。这个功能大概会多损失10%左右的性能,但获得了更高的数据可靠性。所以如果您的Redis服务需要追求极致的性能,就可以将这个选项设置为no。dbfilename:RDB文件在磁盘上的名称。dir:RDB文件的存储路径。默认设置为"./",也就是Redis服务的主目录。
  3-5-2、AOF
  由于是周期性的同步,所以RDB存在的最大问题就是在Redis异常崩溃,需要从最近一次RDB文件恢复数据时,常常出现最近一批更新的数据丢失,而且根据快照的周期设置,这批数据的总量还可能比较大。另外,虽然使用专门的快照进程进行快照数据同步的方式,本身不会造成Redis服务出现卡顿。但如果需要快照的数据量特别大,操作系统基本上会将CPU资源用到快照操作上去,这可能间接造成包括Redis主进程在内的其它进程被挂起。所以,以一个较大的时间周期全部同步Redis数据状态的快照方式,在非常高并发的情况下并不是最好的解决方法。
  虽然RDB快照基本上可以应付我们遇到的大多数业务场景,也可以满足至少80%业务系统设计时的预想性能压力,但为了尽可能解决RDB的工作缺陷,Redis还是提供了另一种数据持久化方式——AOF。AOF全称是Append Only File,从字面上理解就是"只进行增加的文件"。在本专题中,我们在介绍InnoDB的工作过程时,也介绍了类似"只进行增加的文件",就是InnoDB中最关键的重做日志。
  在物理磁盘的操作无论是机械磁盘还是固态磁盘,使用顺序读写都将获得比随机读写好得多的I/O性能。所以我们可以看到无论是关系型数据库、NoSQL数据库还是之前的业务案例,无一例外都追求在物理磁盘上尽可能进行顺序读写操作。
  AOF方式的核心设计思量可以总结为一句话:忠实记录Redis服务启动成功后的每一次影响数据状态的操作命令,以便在Redis服务异常崩溃的情况出现时,可以按照这些操作命令恢复数据状态。既然要记录每次影响数据状态的操作命令,就意味着AOF文件会越来越大!这是必然的。还好Redis为AOF提供了一种重写AOF文件的功能,保证了AOF文件中可以存储尽可能少的操作命令就能保证数据恢复到最新状态,这个功能被称为日志重写功能(请注意这可不是我们在讲解InnoDB时提到的重做日志)。
  举个例子,操作人员在Redis中设置了一个K-V结构:mykey3 = yinwenjie,之后有删除了这个Key信息。那么AOF文件中记录的动作可能如下所示(AOF文件中的内容可以直接通过各种文本编辑工具直接查看):# cat ./appendonly.aof ...... *3 $3 SET $6 mykey3 $12 yinwenjie111 ...... *2 $3 del $6 mykey3 ......
  可以看到以上AOF文件中的内容,如实记录了这两个操作:设置Key和删除Key。但是这种日志记录过程对恢复Key的信息没有任何帮助,因为"mykey3"这个Key信息注定在最新的Redis内存中是不存在的。所以一旦我们运行"重写日志"命令(可以是设定的条件也可以直接运行"BGREWRITEAOF"命令),那么整理后的AOF文件的内容可能就是如下所示了:# cat ./appendonly.aof ...... ......
  在AOF文件中对mykey3这个Key信息的操作过程记录消失了!这不但缩小了AOF文件还没有对数据恢复过程造成任何困扰。Redis主配置文件中关于AOF功能的设定可以在"APPEND ONLY MODE"部分找到:appendonly:默认情况下AOF功能是关闭的,将该选项改为yes以便打开Redis的AOF功能。appendfilename:这个参数项很好理解了,就是AOF文件的名字。appendfsync:这个参数项是AOF功能最重要的设置项之一,主要用于设置"正真执行"操作命令向AOF文件中同步的策略。什么叫"正真执行"呢?还记得我们在本专题中介绍的Linux操作系统对磁盘设备的操作方式吗? 为了保证操作系统中I/O队列的操作效率,应用程序提交的I/O操作请求一般是被放置在Linux Page Cache中的,然后再由Linux操作系统中的策略自行决定正在写到磁盘上的时机。而Redis中有一个fsync()函数,可以将Page Cache中待写的数据真正写入到物理设备上,而缺点是频繁调用这个fsync()函数干预操作系统的既定策略,可能导致I/O卡顿的现象频繁 。
  appendfsync参数项可以设置三个值,分别是:always、everysec、no,默认的值为everysec。
  always参数值,会使得AOF对数据的保存非常稳健。其设置意义是只要有一个写操作命令执行成功,就执行一次fsync函数调用。所以很显然always的设定值,就是三个选项值中处理效率最慢的。
  no参数值,这个设置值表示Redis不会将执行成功的操作命令正真刷入AOF文件,而是完成操作系统级别的写操作后就认为AOF文件记录成功了,后续的I/O操作完全依赖于操作系统的设定,一般30秒会刷一次。
  everysec参数值,这是默认的设置值,也是可以在数据稳健性和性能上平衡较好策略。它表示每秒钟都做一次fsync函数调用,正真做AOF文件的写入操作。no-appendfsync-on-rewrite:always和everysec的设置会使正真的I/O操作高频度的出现,甚至会出现长时间的卡顿情况,这个问题出现在操作系统层面上,所有靠工作在操作系统之上的Redis是没法解决的。为了尽量缓解这个情况,Redis提供了这个设置项,保证在完成fsync函数调用时,不会将这段时间内发生的命令操作放入操作系统的Page Cache(这段时间Redis还在接受客户端的各种写操作命令)。auto-aof-rewrite-percentage:上文说到在生产环境下,技术人员不可能随时随地使用"BGREWRITEAOF"命令去重写AOF文件。所以更多时候我们需要依靠Redis中对AOF文件的自动重写策略。Redis中对触发自动重写AOF文件的操作提供了两个设置:auto-aof-rewrite-percentage表示如果当前AOF文件的大小超过了上次重写后AOF文件的百分之多少后,就再次开始重写AOF文件。例如该参数值的默认设置值为100,意思就是如果AOF文件的大小超过上次AOF文件重写后的1倍,就启动重写操作。auto-aof-rewrite-min-size:参考auto-aof-rewrite-percentage选项的介绍,auto-aof-rewrite-min-size设置项表示启动AOF文件重写操作的AOF文件最小大小。如果AOF文件大小低于这个值,则不会触发重写操作。注意,auto-aof-rewrite-percentage和auto-aof-rewrite-min-size只是用来控制Redis中自动对AOF文件进行重写的情况,如果是技术人员手动调用"BGREWRITEAOF"命令,则不受这两个限制条件左右。
  3-5-3、持久化存储的性能建议
  关于持久化存储的性能建议,我们将结合后文介绍的Redis集群方案一起进行分析
  三、Redis集群方案:高可用
  1、概述
  从本篇文章开始,我们将向读者介绍几种Redis的高可用高负载集群方案。除了介绍Redis 3.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和DUPS_OK_ACK,再例如我们下文介绍的Diskless Replication和Master写保护。
  2-1、主从复制工作过程
  Redis的主从复制功能除了支持一个Master节点对应多个Slave节点的同时进行复制外,还支持Slave节点向其它多个Slave节点进行复制。这样就使得架构师能够灵活组织业务缓存数据的传播,例如使用多个Slave作为数据读取服务的同时,专门使用一个Slave节点为流式分析工具服务。Redis的主从复制功能分为两种数据同步模式:全量数据同步和增量数据同步。
  上图简要说明了Redis中Master节点到Slave节点的全量数据同步过程。当Slave节点给定的run_id和Master的run_id不一致时,或者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文件是有大量I/O过程的,虽然Linux Page Cahe特性会减少性能消耗。
  另外在数据量达到一定规模的情况下,使用全量更新进行和Slave的第一次同步是一个不得已的选择——因为要尽快减少Slave节点和Master节点的数据差异。所以只能占用Master节点的资源和网络带宽资源。
  使用内存记录数据增量操作,可以有效减少Master节点在这方面付出的I/O代价。而做成环形内存的原因,是为了保证在满足数据记录需求的情况下尽可能减少内存的占用量。这个环形内存的大小,可以通过repl-backlog-size参数进行设置。
  Slave重连后会向Master发送之前接收到的Master run_id信息和上一次完成部分同步的offset的位置信息。如果Master能够确定这个run_id和自己的run_id一致且能够在环形内存中找到这个offset的位置,Master就会发送从offset的位置开始向Slave发送增量数据。那么连接正常的各个Slave节点如何接受新数据呢?连接正常的Slave节点将会在Master节点将数据写入环形内存后,主动接收到来自Master的数据复制信息。
  2-2、基本Master/Slave配置
  Redis提供的主从复制功能的配置信息,在Redis主配置文件的"REPLICATION"部分。以下是这个部分的主要参数项说明:slaveof  :如果您需要将某个节点设置为某个Master节点的Slave节点,您需要在这里指定Master节点的IP信息和端口信息。这个设置项默认是关闭的,也即是说Master节点不需要设置这个参数。另外,除了通过配置文件设置外,您还可以通过Redis的客户端命令进行slaveof设定。slave-serve-stale-data:当master节点断开和当前salve节点的连接或者当前slave节点正在进行和master节点的数据同步时,如果收到了客户端的数据读取请求,slave服务器是否使用陈旧数据向客户端提供服务。该参数的默认值为yes。slave-read-only 是否将salve节点设置为"只读"。一旦设置为"只读",表示这个Salve节点只会进行数据读取服务,如果客户端直接向这个Salve节点发送写数据的请求,则会收到错误提示。建议采用默认的"yes"值进行设定。repl-diskless-sync:上文已经介绍过Redis的主从复制功能基于RDB,后者的过程是将数据刷入RDB文件(实际上是Linux的Page Cache区域),然后基于RDB文件内容的更新情况和Salve当前已同步的数据标记点来进行Salve上的数据更新。所以这个过程实际会增加一定的数据延迟,消耗一定的处理资源。基于这个情况,Redis中提供了一种不经过物理磁盘设备就进行主从数据同步的技术,称为diskless。但是直到Redis version 3.2这个技术也一直处于试验状态,所以并不推荐在生产环境下使用:"WARNING: DISKLESS REPLICATION IS EXPERIMENTAL CURRENTLY"。repl-diskless-sync-delay:这个参数只有在上一个参数设置为"yes"时才起作用,主要是设置在进行两次diskless模式的数据同步操作的时间间隔。默认为5秒。repl-ping-slave-period:Slave节点向Master节点发送ping指令的事件间隔,默认为10秒。repl-timeout:这是一个超时间,当某些操作达到这个时间时,Master和Slave双方都会认为对方已经断开连接。实际上您可以将这个时间看成是一个租约到期的时间。那么这个操作时间会影响哪些操作呢?A、向Slave进行的数据同步操作本身不能超过这个时间;B、Slave向Master发送一个PING指令并等待响应的时间;C、Master向Slave发送PONG回复并等待ACK的时间。repl-disable-tcp-nodelay:这个选项的默认值为no,它对优化主从复制时使用的网络资源非常有用。要明白这个参数的含义,就首先要解释一下tcp-nodelay是个什么玩意儿?TCP数据报的报文头包含很多属性,这些属性基本上起到记录和保证传输目的、传输状态的作用,但没有数据报的所携带的业务数据(称之为有效载荷)。那么很明显,20个字节内容的信息分成20个数据报进行传输和只用一个数据报进行传输,需要占用的网络资源就完全不一样。JohnNagle在1984年发明了一种减轻网络传输压力的算法,就是为了解决这个问题(算法的名字就叫做"Nagle",后续的技术人员又做了很多改进和升级)。其基本思路就是将要发送的内容凑够一定的数量后,再用一个数据报发送出去。如果该属性设置为yes,Redis将使用"Nagle"算法(或类似算法),让数据报中的有效载荷凑够一定数量后,在发送出去;设置成no,Redis就不会这么做。repl-backlog-size:上文已经介绍过了Redis中为了进行增量同步所准备的环形内存区域,以及Redis这样做的原因额,所以这里就不再赘述了。这个选项就是用来设置环形内存的大小的,这个选项的默认值为1MB;正式的生产环境下可以稍微加大一些,例如5MB。slave-priority:当前Slave节点的优先级权重。我们后文会介绍一款Redis自带的监控和故障转移工具:Redis Sentinel,这个工具允许一个Master节点下有多个Slave节点,并且可以自动切换Slave节点为Master节点。如果Slave节点的优先级权重值越低,就会再切换时有限成为新的Master节点。min-slaves-to-write和min-slaves-max-lag:为了尽可能避免Master节点对应的多个Slave节点在数据复制过程中数据差异被越拉越大。Redis服务提供了一组拒绝数据写操作的策略,这个策略可以解释为:当Master上在min-slaves-max-lag时间(单位秒)间隔后,任然有min-slaves-to-write个Slave和它正常连接,那么Master才允许进行数据写操作。
  2-3、Master和Slave设置实例
  讨论了Redis中主从复制的基本原理和Redis主配置文件中针对主从复制的设定选项意义后,我们来看一个实际设置过程。注意,由于这个过程非常简单所以我们会"非常快"。首先Master服务器不需要针对主从复制做任何的设置(这不包括对主从复制过程的配置优化)。所以我们就直接来看Slave节点的配置:
  Slave节点上我们只需要做一件事情,就是打开slaveof选项:...... # slaveof选项的设置,给定master节点的ip和port就可以了 # 192.168.61.140就是master节点 slaveof 192.168.61.140 6379 ......
  接着,我们马上就可以看看同步效果了。首先确保您的master节点使工作正常的,然后就可以启动Slave节点了:...... 5349:S 17 Dec 04:20:00.773 * Connecting to MASTER 192.168.61.140:6379 5349:S 17 Dec 04:20:00.773 * MASTER <-> SLAVE sync started 5349:S 17 Dec 04:20:00.774 * Non blocking connect for SYNC fired the event. 5349:S 17 Dec 04:20:00.775 * Master replied to PING, replication can continue... 5349:S 17 Dec 04:20:00.776 * Partial resynchronization not possible (no cached master) 5349:S 17 Dec 04:20:00.782 * Full resync from master: 976f0b31cbf6acd4fcc888301ea4639a7c591136:1 5349:S 17 Dec 04:20:00.864 * MASTER <-> SLAVE sync: receiving 119 bytes from master 5349:S 17 Dec 04:20:00.865 * MASTER <-> SLAVE sync: Flushing old data 5349:S 17 Dec 04:20:00.865 * MASTER <-> SLAVE sync: Loading DB in memory 5349:S 17 Dec 04:20:00.865 * MASTER <-> SLAVE sync: Finished with success 5349:S 17 Dec 04:20:01.068 * Background append only file rewriting started by pid 5352 5349:S 17 Dec 04:20:01.082 * AOF rewrite child asks to stop sending diffs. 5352:C 17 Dec 04:20:01.082 * Parent agreed to stop sending diffs. Finalizing AOF... 5352:C 17 Dec 04:20:01.082 * Concatenating 0.00 MB of AOF diff received from parent. 5352:C 17 Dec 04:20:01.082 * SYNC append only file rewrite performed 5352:C 17 Dec 04:20:01.082 * AOF rewrite: 6 MB of memory used by copy-on-write 5349:S 17 Dec 04:20:01.168 * Background AOF rewrite terminated with success 5349:S 17 Dec 04:20:01.168 * Residual parent diff successfully flushed to the rewritten AOF (0.00 MB) 5349:S 17 Dec 04:20:01.168 * Background AOF rewrite finished successfully ......
  笔者在Slave节点上开启了定期的RDB快照和AOF日志功能,所以各位读者可以忽略那些日志信息,直接关注"Connecting to MASTER …."和"MASTER <-> SLAVE ……."这些日志信息就好。
  以下是Master节点上给出的日志信息...... 5614:M 17 Dec 04:20:00.789 * Slave 192.168.61.145:6379 asks for synchronization 5614:M 17 Dec 04:20:00.789 * Full resync requested by slave 192.168.61.145:6379 5614:M 17 Dec 04:20:00.789 * Starting BGSAVE for SYNC with target: disk 5614:M 17 Dec 04:20:00.791 * Background saving started by pid 5620 5620:C 17 Dec 04:20:00.814 * DB saved on disk 5620:C 17 Dec 04:20:00.815 * RDB: 6 MB of memory used by copy-on-write 5614:M 17 Dec 04:20:00.875 * Background saving terminated with success 5614:M 17 Dec 04:20:00.877 * Synchronization with slave 192.168.61.145:6379 succeeded ......
  看来Master节点收到了Slave节点的连接信息,并完成了全量数据同步操作。
  2-4、关闭RDB功能的说明
  以上介绍的Master节点和Slave节点的设置是否特别简单?是的,实际上只需要打开了Slave节点上"REPLICATION"区域的slaveof选项就可以让Redis的主从复制功能运作起来。现在我们往回倒,回到上一篇文章的介绍。在上一篇文章介绍RDB快照功能的配置项时,文章提到了可以用以下方式关闭RDB快照功能:# 以下为默认的设置为,注释掉即可 # save 900 1 # save 300 10 # save 60 10000 # 在设置以下选项,就可以关闭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、Redis Sentinel
  Redis服务提供了性能较高的主从复制功能,但是没有提供原生的Master——Slave的切换功能。也就是说如果您只是配置了Redis的主从复制功能,那么在Master节点出现故障时,您必须手动将一台Slave状态的节点切换为Master状态。当然这个问题在Redis Version 2.8 版本前是有标准解决方案的,那就是:Keepalived + Redis服务组成的高可用集群。
  由Keepalived监控Redis高可用集群中Master节点的工作状态,并在异常情况下切换另一个节点接替工作。但是,这个方案是有一些问题了,其中之一就是所有的Slave节点在Standby状态时无法分担Master节点的任何性能压力——即使您设置了read-only等参数也不行,因为VIP根本不会把请求切过去。并且这种方式还不太方便监控Redis高可用集群中各个服务节点的实时状态。
  从Version 2.8版本开始,Redis提供了一个原生的主从状态监控和切换的组件——Redis Sentinel。通过它技术人员不但可以完成Redis高可用集群的适时监控,还可以通过编程手段减轻集群中Master节点中读操作的压力。本节内容,我们向读者介绍这个Redis Sentinel的简单使用。
  3-1、基本配置
  由于Redis Sentinel是Redis原生支持的,以Redis Version 3.2为例,在下载安装后就可以直接使用命令"redis-sentinel"启动Sentinel了。Sentinel的主配置文件模板存放在Redis安装目录的下,默认名为"sentinel.conf"。以下命令可以启动Sentinel(启动Sentinel所依据的配置文件是一定要携带的参数):# redis-sentinel ./sentinel.conf
  Redis Sentinel本身也支持集群部署,而且为了在生产环境下避免Sentinel单点故障,所以也建议同时部署多个Sentinel节点。部署多个Sentinel还有一个原因,就是提高Master——Slave切换的准确性。以下的配置文件介绍会说明这一点。
  下面我们介绍一些Sentinel主配置文件中的关键配置,注意Sentinel主配置文件也有类似Redis主配置文件提供的访问保护模式(protected-mode)、访问者权限控制(auth-pass)等,但是它们的意义基本上类似前文介绍过的,在Redis主配置文件中的相似内容,所以这里就不再赘述了。
  sentinel monitor    :
  这个属性是Redis Sentinel中的最主要设置元素,换句话说如果要开启Sentinel甚至可以只设置这个属性。它包括了四个参数:master-name,这个参数是一个英文名说明了Sentinel服务监听的Master节点的别名,如果一个Sentinel服务需要同时监控多个Master,这需要设置多个不同的master-name;ip和redis-port,指向sentinel需要监控的Redis集群最初的那个Master节点(为什么会是最初呢?后文会说明)的ip和端口;quorum,投票数量这个参数很重要,如果是Sentinel集群方式下,它设定"当quorum个Sentinel认为Master异常了,就判定该Master真的异常了"。单个Sentinel节点认为Master下线了被称为主管下线,而quorum个Sentinel节点都认为Master下线的情况被称为客观下线。
  sentinel parallel-syncs  :
  一旦原来的Master节点被认为客观下线了,Sentinel就会启动切换过程。大致来讲就是从当前所有Slave节点选择一个节点成为新的Master节点(这时在Redis中设定的slave-priority参数就会起作用了)。而其它的Slave其slaveof的Master信息将被sentinel切换到新的Master上。而一次同时并行切换多少个Slave到新的Master上就是这个参数决定的。如果整个Redis高可用集群的节点数量不多(没有超过6个),建议使用默认值就可以了。
  主配置文件中被rewrite的参数内容:sentinel.conf文件中的配置内容会随着Sentinel的监控情况发生变化——由Sentinel程序动态写入到文件中。例如sentinel known-slave参数、sentinel current-epoch参数和sentinel leader-epoch参数。
  注意,在Sentinel中您只需要配置最初的Master的监控位置,无需配置Master下任何Slave的位置,Sentinel会自己识别到这些Master直接的或者间接的Slave。
  3-2、切换效果
  介绍完配置后,我们来简单看一个Sentinel工作和切换的例子。这个例子中的有一个Master节点和一个Slave节点,当Master节点出现故障时,通过Sentinel监控到异常情况并自动完成Slave状态的切换。
  首先请保证您的Master节点和Slave节点都是正常工作的,这个过程可以参见笔者之前文章的介绍:
  这里就不再赘述Redis Master和Redis Slave的内容了,因为在本文第2节中已经详细介绍过。实际上您只需要打开Slave节点的主配置文件,并增加slaveof的配置信息,将其指向Master的IP和端口就可以了。以下是Sentinel节点主要更改的配置信息:...... sentinel monitor mymaster 192.168.61.140 6379 1 ......
  由于在测试环境中我们只使用了一个Sentinel节点,所以设置sentinel monitor配置项中的quorum为1就可以了,代表有一个Sentinel节点认为Master不可用了,就开启故障转移过程。当然生产环境下不建议这样使用。
  之后我们使用以下Sentinel的主要配置信息启动Sentinel:# redis-sentinel ./sentinel.conf ...... 8576:X 19 Dec 00:49:01.085 # Sentinel ID is 5a5eb7b97de060e7ad5f6aa20475a40b3d9fd3e1 8576:X 19 Dec 00:49:01.085 # +monitor master mymaster 192.168.61.140 6379 quorum 1 ......
  之后主动终止原来Master的运行过程(您可以直接使用kill命令,或者拔掉网线,又索性直接关机),来观察Slave节点和Sentinel节点的日志情况:
  当断开原来的Master节点后,Slave节点将提示连接失效并开始重试。当Sentinel开始进入故障转移并完成后,Salve又会打印相应的过程信息:...... 8177:S 19 Dec 00:53:17.467 * Connecting to MASTER 192.168.61.140:6379 8177:S 19 Dec 00:53:17.468 * MASTER <-> SLAVE sync started 8177:S 19 Dec 00:53:17.468 # Error condition on socket for SYNC: Connection refused ...... 8177:M 19 Dec 00:53:18.134 * Discarding previously cached master state. 8177:M 19 Dec 00:53:18.134 * MASTER MODE enabled (user request from "id=3 addr=192.168.61.140:51827 fd=5 name=sentinel-5a5eb7b9-cmd age=258 idle=0 flags=x db=0 sub=0 psub=0 multi=3 qbuf=0 qbuf-free=32768 obl=36 oll=0 omem=0 events=r cmd=exec") 8177:M 19 Dec 00:53:18.138 # CONFIG REWRITE executed with success. ......
  从以上Slave节点的内容可以看到,Slave被切换成了Master状态。那么Sentinel本身有哪些重要的日志信息呢?如下所示:...... // 当前Sentinel节点确定原Master主观下线 8576:X 19 Dec 00:53:18.074 # +sdown master mymaster 192.168.61.140 6379 // 由于设置的quorum为1,所以一个Sentinel节点的主管下线就认为Master客观下线了 8576:X 19 Dec 00:53:18.074 # +odown master mymaster 192.168.61.140 6379 #quorum 1/1 // 第三代,每转移一次故障epoch的值+1, // 不好意思,在书写测试实例前,本人已经自行测试了两次故障转移,所以这里看到的epoch为3 // 这个信息会自动写入到Sentinel节点的主配置文件中 8576:X 19 Dec 00:53:18.074 # +new-epoch 3 // 开始进行故障转移 8576:X 19 Dec 00:53:18.074 # +try-failover master mymaster 192.168.61.140 6379 // 选举出主导故障转移的Sentinel节点,因为不是所有Sentinel节点都会主导这个过程 8576:X 19 Dec 00:53:18.084 # +vote-for-leader 5a5eb7b97de060e7ad5f6aa20475a40b3d9fd3e1 3 8576:X 19 Dec 00:53:18.084 # +elected-leader master mymaster 192.168.61.140 6379 8576:X 19 Dec 00:53:18.084 # +failover-state-select-slave master mymaster 192.168.61.140 6379 // 选择提升哪一个slave作为新的master 8576:X 19 Dec 00:53:18.156 # +selected-slave slave 192.168.61.145:6379 192.168.61.145 6379 @ mymaster 192.168.61.140 6379 8576:X 19 Dec 00:53:18.156 * +failover-state-send-slaveof-noone slave 192.168.61.145:6379 192.168.61.145 6379 @ mymaster 192.168.61.140 6379 8576:X 19 Dec 00:53:18.211 * +failover-state-wait-promotion slave 192.168.61.145:6379 192.168.61.145 6379 @ mymaster 192.168.61.140 6379 // 提升原来的slave 8576:X 19 Dec 00:53:19.201 # +promoted-slave slave 192.168.61.145:6379 192.168.61.145 6379 @ mymaster 192.168.61.140 6379 // 试图重写所有salves节点的配置信息,并让它们指向新的master 8576:X 19 Dec 00:53:19.201 # +failover-state-reconf-slaves master mymaster 192.168.61.140 6379 // 故障转移结束 8576:X 19 Dec 00:53:19.250 # +failover-end master mymaster 192.168.61.140 6379 // 最终完成master节点的切换 8576:X 19 Dec 00:53:19.250 # +switch-master mymaster 192.168.61.140 6379 192.168.61.145 6379 // 注意原有的master节点会再显示一条作为主观下线,但是这次下线信息是以salve身份通知的 // 这是因为这次故障切换后,原来的master就算再上线,也只会作为Slave节点了 8576:X 19 Dec 00:53:19.251 * +slave slave 192.168.61.140:6379 192.168.61.140 6379 @ mymaster 192.168.61.145 6379 8576:X 19 Dec 00:53:49.305 # +sdown slave 192.168.61.140:6379 192.168.61.140 6379 @ mymaster 192.168.61.145 6379 ......
  通过Slave节点和Sentinel节点的日志可以看到,在经过了短暂的时间后Sentinel成功将唯一一个Slave节点转换成了Master节点,并继续向外部提供服务。
  之后我们重新启动原有Master节点,看看会发生什么:# 以下是原有Master启动后,在Sentinel显示的信息 ...... 8576:X 19 Dec 01:31:12.743 * +reboot slave 192.168.61.140:6379 192.168.61.140 6379 @ mymaster 192.168.61.145 6379 8576:X 19 Dec 01:31:12.805 # -sdown slave 192.168.61.140:6379 192.168.61.140 6379 @ mymaster 192.168.61.145 6379 ......
  一个非常重要的现象是,当原来的Master节点再次启动时,即使配置文件中没有设定slaveof信息,它也会在Sentinel的协调下称为Slave节点。这是因为任何一次Master到Slave的切换都是要付出代价的,其中除了状态本身的判断外,还有Sentinel自身协调和选举过程(选举哪一个Sentinel进行实质的切换动作),还有新的Master的选定问题,甚至包括Slave的slaveof目标变化过程中需要处理的数据一致性问题等等工作。所以最好的办法就是:只要能够保证Redis高可用集群持续工作,就不进行Master状态的切换。
  3-3、Java客户端配合Sentinel的使用
  通过Sentinel组建的高可用集群对比通过第三方软件组建的高可用集群而言,有其明显的优点。例如可以实时返回集群中每个Redis节点的状态,且各节点间更能保持最佳的数据一致性,另外还可以在必要的时候通过转移客户端读操作,减轻Master节点的工作压力。但是它也有一个很明显的缺点,就是由于整个集群可以向调用者开放多个Redis节点的地址,且Sentinel本身并不能充当路由器的作用,所以当Redis高可用集群进行状态切换时,客户端可能并不清楚原有的Master节点已经失效了。如下图所示:
  还好的是,Java最常用的Redis客户端jedis提供了一组针对Sentinel的集群工具,让客户端可以在获取当前Redis高可用集群中的Master节点后,再在这个Master节点上完成数据读写操作。但另外一个读操作的负载问题还是没有被解决,所有的读操作也只会在Master节点完成。
  我们来看看一些关键代码:...... // 这是基本的连接配置 // 当让这些属性都可以根据您的实际情况进行更改 JedisPoolConfig poolConfig = new JedisPoolConfig(); poolConfig.setMaxTotal(100); poolConfig.setMaxIdle(50); poolConfig.setMinIdle(20); poolConfig.setMaxWaitMillis(6 * 1000); poolConfig.setTestOnBorrow(true); // 以下是可用的多个Sentinel节点的ip和端口 Set jedisClusterNodes = new HashSet(); jedisClusterNodes.add("192.168.61.140:26379"); //如果有多个Sentinel一个一个添加进去 //jedisClusterNodes.add("192.168.61.139:26379"); JedisSentinelPool jedisSentinelPool = new JedisSentinelPool("mymaster", jedisClusterNodes , poolConfig); // 开始插入信息 for(Integer index = 0 ; index < 10000 ; index++) { // 获取最新的master信息 Jedis master = null; try { master = jedisSentinelPool.getResource(); } catch(JedisConnectionException e) { // 如果出现异常,说明当前的maser断开了连接,那么等待一段时间后重试 LOGGER.info("master is loss , waiting for try again......"); synchronized (MasterSlaveApp.class) { MasterSlaveApp.class.wait(5000); } index--; continue; } // 开始正式插入 master.set(("key" + index).getBytes(), index.toString().getBytes()); LOGGER.info("write : " + "key" + index); 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] INFO redis_test.test.MasterSlaveApp - write : key29 16144 [main] INFO redis_test.test.MasterSlaveApp - master is loss , waiting for try again...... 22148 [main] INFO redis_test.test.MasterSlaveApp - master is loss , waiting for try again...... 28151 [main] INFO redis_test.test.MasterSlaveApp - master is loss , waiting for try again...... 34155 [main] INFO redis_test.test.MasterSlaveApp - master is loss , waiting for try again...... 40159 [main] INFO redis_test.test.MasterSlaveApp - master is loss , waiting for try again...... 十二月 20, 2016 4:12:22 下午 redis.clients.jedis.JedisSentinelPool initPool 信息: Created JedisPool to master at 192.168.61.145:6379 46163 [main] INFO redis_test.test.MasterSlaveApp - master is loss , waiting for try again...... 51166 [main] INFO redis_test.test.MasterSlaveApp - write : key30 51670 [main] INFO redis_test.test.MasterSlaveApp - write : key31 ......
  四、Redis集群方案:高性能
  1、概述
  从这篇文章开始我们一起来讨论Redis中两种高性能集群方案,并且在讨论过程中将上一篇文章介绍的高可用集群方案结合进去。这两种高性能集群方案是:Twemproxy和Redis自带的Cluster方案。
  2、Redis高性能集群:Twemproxy
  2-1、Twemproxy概要
  Twemproxy是一个Twitter开源的一个Redis/Memcache代理服务器,最早也是Twitter在使用。在Twitter决定开发Twemproxy时,互联网领域使用最广泛的缓存技术还是Memcache,那个时候Redis并没有提供原生的Cluster功能,甚至没有Bate版本。而Twemproxy(也称为nutcraker)恰恰是为了解决将多个独立的Redis节点组成集群,同时提供缓存服务的问题。
  您可以在GitHub上下载Twemproxy(https://github.com/twitter/twemproxy)。这个地址也可以算作Twemproxy的官网了,其上对Twemproxy的特性进行了简要描述,包括:轻量级的快速访问代理、减少客户端对Redis服务的直连、支持多个Redis节点同时工作、支持数据分片、支持多种Hash算法、故障检查和故障节点自动排除、支持多种操作系统Linux, BSD, OS X 以及 Solaris…… 。
  但是官网上并没有明确介绍Twemproxy的缺点,而且细心的朋友可以观察到官网上的源代码已经有相当时间没有更新了,这能说明什么问题呢?本文后续内容将进行说明。下图说明了Twemproxy的功能定位:
  2-2、Twemproxy基本配置
  Twemproxy的基本配置非常简单,直接解压安装后就可以运行。它的主要配置文件在conf目录下的"nutcracker.yml"文件。由于Twemproxy的安装太简单了,本文就不再进行描述了,这里给出安装命令就可以了(CentOS 6.X / 7.X):// 先安装automake、libtool等第三方支持组件 # yum install -y automake libtool // 然后解压下载的Twemproxy压缩包 // 进入解压的目录后运行(生成configure ) # autoreconf -fvi // 正式开始安装 # ./configure --prefix=您的安装目录
  安装后打开"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、fnv1_64、fnv1a_64、fnv1_32、fnv1a_32、hsieh、murmur、jenkins等。之前我们介绍过,考虑采用哪种Hash算法有两个重要指标:Hash算法的速度和Hash算法的碰撞率。例如之前号称破解了MD5算法的王小云教授就是依靠Hash碰撞完成的,但实际上这种方式算不算完全破解呢?行业内就有很多种观点了,这里笔者经验有限就不展开讨论了。但MD5算法的两个事实依然是存在的:MD5任然是不可逆的,同时现成的破解站点也是存在的:http://www.ttmd5.com/。另外两个事实是MD5算法的碰撞率确实是各种Hash算法中碰撞率非常低的,但它确实不是所有Hash算法中速度最快的。显然,这里我们更看重的Hash值的计算速度而不是碰撞率,产生Hash碰撞的两个Key其后果无非是被分配到同一个Redis节点进行存储而已。所以这里我们推荐设置两种Hash算法,murmur和fnv1a_64。hash_tag:为了避免在业务级别有关联的数据因为Key的Hash值不同而被散落在不用的Redis服务上(原因是当存储部分相关联数据的Redis下线,这个完整的业务数据就会受到影响),Redis提供了一个设置参数hash_tag来框定一个Key的部分字符串,并对它进行Hash计算。这样就可以保证有关联的业务数据在进行Hash计算时得到同一个计算结果,从而被分配到一个Redis节点进行存储。hash_tag由两个字符组成,举个例子,设置hash_tag为"[]",这时客户端通过Twemproxy存储两个Key:"user[yinwenjie]"、"sex[yinwenjie]",那么当Twemproxy计算这两个Key的Hash值时,就只会采用"[]"中的字符串"yinwenjie"进行,所以计算出来的Hash值都是一样的。最终这两个Key都会落到同一个Redis节点上进行存储。distribution:依据Key的数据分配模式。Twemproxy本身并不存储数据,它的一个重要功能就是依据客户端传来的Key,对存储数据的真实Redis节点进行定位。定位方式包括三种:ketama,一致性Hash算法,关于一致性Hash算法的介绍可以参考这篇文章:http://blog.csdn.net/yinwenjie/article/details/46620711#t1。modula,这种数据分配模式,是根据Key的Hash值取模,模的数量就是下层可工作的Redis节点数量。random,完全随机分配Key对应的真实Redis节点。timeout:这是一个超时时间,用来指定等待和Redis建立连接的超时时间,以及从Redis收到响应的超时时间。backlog:"The TCP backlog argument. Defaults to 512."这是官网上的解释,很简单不是吗?实际上我们介绍Redis时也出现过类似的参数,这个参数允许当前同时进行连接的有效TCP连接数量,但是请配合Linux系统下的somaxconn的设置进行使用,否则它会失效。preconnect:这个参数的默认值为false,主要指代当Twemproxy服务启动时,是否需要预连接到下层的Redis服务上。redis、redis_auth和redis_db: Twemproxy可以作为Redis和Memcache两种缓存服务的代理,当redis参数设置为false时代表它将作为Memcache的代理。另外如果下层的Redis设置了权限验证信息,则Twemproxy还要通过redis_auth配置项进行相应的设置。最后,由于Redis支持多个数据库,那么Twemproxy默认情况下将提供编号为"0"的数据库的代理,如果要改变请通过redis_db参数进行设置。server_connections:这个参数设置Twemproxy可以在每一个下层Redis/Memcache服务上同时使用的连接数量,默认的值为1。auto_eject_hosts、server_failure_limit和server_retry_timeout:Twemproxy支持自动下线(不再代理)失败的Redis服务,如果要打开这个功能,请设置auto_eject_hosts参数为true。这时,Twemproxy会在重试server_failure_limit次数后将还没有连接测试成功的Redis服务从自身代理列表上去掉。而server_retry_timeout设置了每一次测试连接的等待超时时间。servers:这个参数是一个列表,列出了Twemproxy代理的Redis的IP地址、访问端口和权重。
  以下展示了一个完整的可以使用的Twemproxy代理的配置文件:beta: listen: 0.0.0.0:22122 hash: fnv1a_64 hash_tag: "{}" distribution: ketama auto_eject_hosts: false timeout: 400 redis: true servers: - 192.168.61.140:6379:1 server1 - 192.168.61.145:6379:1 server2 #原有配置文件中的其它配置信息如果不使用则可以注释掉
  以上配置信息中的各个属性已经在前文详细介绍,这里就不再赘述了。以下是Twemproxy的启动指令,记得要首先设置Linux下的环境变量:# nutcracker -c ./nutcracker.yml // 您还可以通过以下命令测试配置文件的正确性 # nutcracker -c ./nutcracker.yml -t // 还有更多参数可选 Usage: nutcracker [-?hVdDt] [-v verbosity level] [-o output file] [-c conf file] [-s stats port] [-a stats addr] [-i stats interval] [-p pid file] [-m mbuf size] // 关于这些参数更详细的使用说明,可以参考官方文档中的说明
  2-3、LVS + Twemproxy + Keepalived + Redis + Redis Sentinel + Sentinel Agent
  在生产环境下搭建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提供了一个配合使用的扩展组件:Redis_Twemproxy_Agent,它的作用是监控Sentinel中Master节点的情况,并且将最新的Master节点情况通知Twemproxy。这样一来当下层某组Redis高可用集群发生Master—Slave状态切换时,Twemproxy就会适时对其下层代理配置情况作出调整。
  另外,上图中给出的第二种生产环境下的Redis集群方案,一共有5组独立运行的Redis高可用集群组,每组Redis高可用集群都有一个Master节点和至少一个Slave节点,它们之间使用Redis原生提供的数据复制功能保持数据同步。最后这些Redis高可用集群组通过一组Sentinel进行状态监控,而这组Sentinel也是同时拥有一个Master节点和两个Slave节点的高可用集群。
  3、Redis高性能集群:Redis Cluster
  3-1、Twemproxy的生产环境问题
  可维护性上的问题:
  LVS + Twemproxy + Keepalived + Redis + Sentinel + Sentinel Agent 的架构方案应该是...

太阳报若日尼奥前往美国度假,背着女友注册约会软件直播吧1月28日讯根据英国媒体太阳报消息,切尔西球星若日尼奥在英超假期前往美国度假,他还注册了一个约会软件。英超迎来间歇期,若日尼奥在此期间前往美国佛罗里达州度假,他没有带上女友和太阳报若日尼奥前往美国度假,背着女友注册约会软件直播吧1月28日讯根据英国媒体太阳报消息,切尔西球星若日尼奥在英超假期前往美国度假,他还注册了一个约会软件。英超迎来间歇期,若日尼奥在此期间前往美国佛罗里达州度假,他没有带上女友和邓肯场外有多惨?妻子帮情郎创业,被骗走2000万,幸好遇到瓦妮莎世界上永恒的定律,最后受伤的只有老实人,邓肯就是如此。1997年选秀大会,马刺用手中的状元签选中邓肯,这一用就是19个赛季,期间马刺无一例外全部进入季后赛,九次西部决赛,六次总决赛被骂滚回中国,华裔拳手霸气还击近日,美国洛杉矶的亚凯迪亚(Arcadia)市发生一起亚裔种族歧视事件。华裔拳手杨连慧被一名流浪汉两度上门挑衅,后者甚至还放狗咬人。不过令人解气的是,被接连挑衅后,杨连慧拿起棍棒回莫科再婚后不忘前妻,带现任妻子照顾前岳父母,至今仍在坚持他是篮球场上的小狼王他和杜锋被人合称为南杜锋,北莫科作为篮球场上一名闪耀的明星爱妻结婚不到五月便与世长辞莫科捧着妻子的遗像,一夜间霜雪满头。妻子离世,他把岳父岳母当做自己的亲生父母图文集乒坛伟大的奥运亚军妻子貌美如花家庭温馨浪漫而幸福伟大的奥运亚军王皓提起我国乒坛的王皓,大家对他印象最深的就是三次参加奥运会三次打进男单决赛,三次痛失金牌,屈居亚军的乒坛往事。2004年的雅典奥运会是王皓首次参加奥运会,决赛对手是詹姆斯职业生涯中已与六对父子二人组交手过,期待与儿子同场竞技美媒列出詹姆斯在其职业生涯中已与六对父子二人组交手过勒布朗詹姆斯是稳定和统治的代名词。这位洛杉矶湖人队的超级巨星不断地证明怀疑者是错误的,当每个人都认为他正在放慢速度时,他还在最高追星大赢家!欧洲冠军儿子晒与国乒7大奥运冠军合影,太令人羡慕在最近结束的澳门站赛事中,中国乒乓球运动员的表现非常出色,尤其是在比赛中很多选手都展现出了超强的实力,年轻球员的出色发挥,令大家眼前一亮,他们都是中国乒乓球队的有生力量,将会在之后邓肯场外有多惨?妻子帮情郎创业,被骗走2000万,幸好遇到瓦妮莎世界上永恒的定律,最后受伤的只有老实人,邓肯就是如此。1997年选秀大会,马刺用手中的状元签选中邓肯,这一用就是19个赛季,期间马刺无一例外全部进入季后赛,九次西部决赛,六次总决赛被骂滚回中国,华裔拳手霸气还击近日,美国洛杉矶的亚凯迪亚(Arcadia)市发生一起亚裔种族歧视事件。华裔拳手杨连慧被一名流浪汉两度上门挑衅,后者甚至还放狗咬人。不过令人解气的是,被接连挑衅后,杨连慧拿起棍棒回莫科再婚后不忘前妻,带现任妻子照顾前岳父母,至今仍在坚持他是篮球场上的小狼王他和杜锋被人合称为南杜锋,北莫科作为篮球场上一名闪耀的明星爱妻结婚不到五月便与世长辞莫科捧着妻子的遗像,一夜间霜雪满头。妻子离世,他把岳父岳母当做自己的亲生父母
女演员玛雅霍克在圣巴特海边游泳,穿白色连身装玩得很开心近日,玛雅霍克(MayaHawke)现身圣巴特海滩度假,当天,她身穿一件白色的连体装出现在海边,她喜欢游泳运动,经常会到海边来游泳,她的身材高挑有型,非常有魅力。玛雅霍克(Maya联盟第2联盟第1!欧文末节风暴重回巅峰,篮网双剑合璧冲冠北京时间1月1日,篮网123比106击败黄蜂取得11连胜,球队战绩24胜12负升到东部第二,同时也是全联盟第二,仅仅落后凯尔特人2个胜场。在这波11连胜当中,欧文发挥了巨大作用。他广东标价8亿出售,马布里下课!周琦海南度假,辽篮考察考辛斯头条创作挑战赛关于有CBA球队挂牌8亿人民币转让一事近来持续发酵,球迷们好奇的是哪支球队可以转让到如此高的价格,纵观当今中国篮坛,目前想要出售且能出售到这个价格的,有且只有11冠王恒大足球降级了,恒大房子又会不会降级呢?中国足球又上热搜了!这次的热搜是因为广东省运会的假球案处理结果出来了,16位官员被处理。紧接又有第二条热搜,今次的主角是恒大!广州恒大队提前一轮降级。恒大足球降级了,那恒大的房子又哈里伯顿24710,特纳34分,步行者主场险胜快船,乔治空砍45分北京时间2023年1月1日,NBA常规赛照常进行,步行者坐镇主场迎战前来挑战的快船队!战罢,步行者131130力克快船。两队首发步行者队哈里伯顿,特纳,希尔德,内史密斯,内姆布哈德金主进入,泰山引援开启,第一目标不是韦世豪,当红国脚补强短板中超曲终人散,在一地鸡毛之中,武汉三镇以净胜球优势夺得本赛季中超冠军,而卫冕冠军山东泰山则遗憾地再次卫冕失败。对于山东球迷而言,山东泰山本赛季卫冕失败,不是丢掉一个冠军的问题,刚刚高合HiPhiZ这么贵,注定不属于老百姓,是有钱人的大玩具2019年,新能源市场的蛋糕已经被瓜分完毕,特别是20万左右的市场,基本上被特斯拉蔚来小鹏等品牌霸占,这一年刚成立的新势力品牌,想要谋求生存,只能另寻出路,拔高市场定位,售卖价格更今天起,这些新规开始执行律回春晖渐,万象始更新。新的一年开启新的征程,也带来了新的变化新的利好新的希望。2023年1月1日起,一系列汽车行业相关的新政策新规范新举措开始实施,不仅会对汽车产业带来新的影响,纯国产32核CPU发布苹果M1相同工艺日前某国产芯片CPU厂商已完成32核服务器CPU初样芯片验证,目前将马上交付给PC以及服务器厂商,官方信息显示,这颗全新的CPU名为3D5000的芯片,是将两枚原本16核心的国产C企业服务云本方案集成了互联网大数据物联网云计算卫星导航人工智能等新兴技术,以北斗GNSS高精度定位技术为依托,包括农业智能化生产装备终端和一个数字化管控平台。农业智能化生产装备终端涵盖农业生关于Linux下HAProxy自动化部署的一些笔记整理写在前面学习OKD,里面的路由组件通过HAproxy实现所以这里学习HAproxy,博文内容涉及手工部署HAproxy编写Ansible角色自动部署HAproxy理解不足小伙伴帮忙