在优酷,咱们使用 Redis Cluster 构建了一套内存存储系统,项目代号为蓝鲸。蓝鲸的设计目标是高效读写,全部数据都在内存中。蓝鲸的主要应用场景是 cookie 和大数据团队计算的数据,都具备较强的时效性,所以全部的数据都有过时时间。更准确的说蓝鲸是一个全内存的临时存储系统。前端
到目前为止集群规模逐渐增加到 700+ 节点,即将达到做者建议的最大集群规模 1,000 节点。咱们发现随着集群规模的扩大,带宽压力不断突出,而且响应时间 RT 方面也会略微升高。与一致性哈希构建的 Redis 集群不同,Redis Cluster 不能作成超大规模的集群,它比较适合做为中等规模集群的解决方案。node
运维期间,吞吐量与 RT 一直做为衡量集群稳定性的重要指标
,这里在本文中,咱们碰到的影响集群吞吐量与 RT 的一些问题与探索记录下来,但愿对你们有所帮助。redis
Redis 采用单进程模型
,除去 bgsave 与 aof rewrite 会另外新建进程外,全部的请求与操做都在主进程内
完成。其中比较重量级的请求与操做类型有:数据库
Redis 服务端采用Reactor 设计模式
,它是一种基于事件的编程模型
,主要思想是将请求的处理流程
划分红有序的事件序列
,好比对于网络请求一般划分为:Accept new connections
、Read input to buffer
、Process request
、 Response
等几个事件。并在一个无限循环的 EventLoop 中不断的处理这些事件。更多关于Reactor,请参考 https://en.wikipedia.org/wiki/Reactor 。编程
比较特别的是,Redis 中还存在一种时间事件,它实际上是定时任务,与请求事件同样,它一样在 EventLoop 中处理。Redis 主线程的主要处理流程以下图:设计模式
理解了 Redis 的单进程模型与主要负载状况,很容易明白,想要增长 Redis 吞吐量,只须要尽可能下降其它任务的负载量就好了,因此提升 Redis 集群吞吐量的方式主要有:缓存
A) - 提升 Redis 集群吞吐的方法安全
cluster-node-timeout
参数咱们发现当集群规模达到必定程度时,集群间消息通信开销的带宽是极其可观的。cookie
集群通讯机制网络
Redis 集群采用无中心的方式,为了维护集群状态统一,节点之间须要互相交换消息。Redis采用交换消息的方式被称为 Gossip
,基本思想是节点之间互相交换信息最终全部节点达到一致,更多关于 Gossip 可参考 https://en.wikipedia.org/wiki/Gossip_protocol 。
总结集群通讯机制的一些要点:
如何理解集群中节点状态的十分之一?假如集群中有 700 个节点,十分之一就是 70 个节点状态,节点状态具体数据结构见下边代码:
咱们将注意力放在数据包大小与流量上,每一个节点状态大小为 104 byte,因此对于 700 个节点的集群,这部分消息的大小为 70 * 104 = 7280,大约为 7KB。另外每一个 Gossip 消息还须要携带一个长度为 16,384 的 Bitmap,大小为 2KB,因此每一个 Gossip 消息大小大约为 9KB。
随着集群规模的不断扩大,每台主机的流量不断增加,咱们怀疑集群间通讯的流量已经大于前端请求产生的流量,因此作了如下实验以明确集群流量情况。
实验过程
实验环境为:节点 704,物理主机 40 台,每台物理主机有 16 个节点,集群采用一主一从模式,集群中节点 cluster-node-timeout 设置为 30 秒。
实验的大概思路为,分别截取一分钟时间内一个节点,在集群通讯端口上,进入方向与出去方向的流量,并统计出消息条数,并最终计算出台主机由于集群间通信产生的带宽开销。实验具体过程以下:
经过实验能看到进入方向与出去方向在 60s 内收到的数据包数量为 2,700 多个。由于 Redis 规定每一个节点每一秒只向一个节点发送数据包,因此正常状况每一个节点平均 60s 会收到 60 个数据包,为何会有这么大的差距?
原来考虑到 Redis 发送对象节点的选取是随机的,因此存在两个节点好久都没有交换消息的状况,为了保证集群状态能在较短期内达到一致性,Redis 规定当两个节点超过 cluster-node-timeout 的一半时间没有交换消息时,下次心跳交换消息。
解决了这个疑惑,接下来看带宽状况。先看 Redis Cluster 集群通讯端口进入方向每台主机的每秒带宽为:
通过以上实验咱们能得出两个结论:
集群间通讯占用大量带宽资源 调整 cluster-node-timeout 参数能有效下降带宽
Redis Cluster 断定节点为 fail 的机制
可是并非 cluster-node-timeout 越大越好。当 cluster-node-timeou 增大的时候集群判断节点 fail 的时间会增长,从而 failover 的时间窗口会增长。集群断定节点为fail所需时间的计算公式以下:
当节点向失败节点发出 PING 消息,而且在 cluster-node-timeout 时间内尚未收到失败节点的 PONG 消息,此时断定它为 pfail 。pfail 即部分失败,它是一种中间状态,该状态随着集群心跳不断传播。再通过一半 cluster-node-timeout 时间后,全部节点都与失败的节点发生过心跳而且把它标记为 pfail 。固然也可能不须要这么长时间,由于其它节点之间的心跳一样会传递 pfail 状态,这里姑且以最大时间计算。
Redis Cluster 规定当集群中超过一半以上节点认为一个节点为 pfail 状态时,会把它标记为 fail 状态,并广播给其余全部节点。对于每一个节点而言平均一秒钟收到一个心跳包,每次心跳都会携带随机的十分之一的节点个数。因此如今问题抽像为通过多长时间一个节点会积累到一半的 pfail 状态数。这是一个几率问题,由于我的并不擅长几率计算,这里直接取了一个较大几率能知足条件的数值 10。
因此上述公式不是达到这么长时间必定会断定节点为 fail,而是通过这么长时间集群有很大几率会断定节点 fail 。
Redis Cluster 默认 cluster-node-timeout 为 15s,咱们将它设置成了 30s。也就是说 700 节点的集群,集群间带宽开销为 104.5MBit / s,断定节点失败时间窗口大概为 55s,实际上大多数状况都小于 55s,由于上边的计算都是按照高位时间估算的。
总而言之,对于大的 Redis 集群 cluster-node-timeout 参数的须要谨慎设定。
提升 Redis 集群吞吐的方法 2. 控制主节点写命令传播
Redis 中主节点的每一个写命令传播到如下三个地方:
本地 AOF 文件,以持久化持数据 主节点的全部从节点,以保持主从数据同步 本节点的 repl_backlog 缓存,主要为了支持部分同步功能,详见官网 Replcation 文档 Partial resynchronization 部分:http://redis.io/topics/replication
其中 repl_backlog 部分传播在 replicationFeedSlaves 函数中完成。
减小从节点的数量
高可用的集群不该该出现单点,因此 Redis 集群通常都会是主从模式。Redis 的主从同步机制是全部的主节点的写请求,会同步到全部的从节点。若是没有从节点,对于主节点来讲,它只须要处理该请求便可。但对于有 N 个从节点的主节点来讲,它须要额外的将请求传播给 N 个从节点。请注意这里是对于每一个写请求都会这样处理。显而易见从节点的数量对主节点的吞吐量的影响是比较大的,咱们采用的是一主一从模式。
由于从节点不须要同步数据,生产环境中观察主节点的 CPU 占用率要比从节点机器要高,这对这条结论起到了佐证的做用。
关闭 AOF 功能
若是开启 AOF 功能,每一个写请求都会 Append 到本地 AOF 文件中,虽然 Linux 中写文件操做会利用到操做系统缓存机制,可是若是关闭 AOF 功能主线程中省去了写 AOF 文件的操做,显然会对吞吐量的增长有帮助。
AOF 是 Redis 的一种持久化方式,若是关闭了 AOF 功能怎么保证数据的安全性。咱们的作法是定时在从节点 BGSAVE。固然具体采用何种策略须要结合具体状况来决定。
去掉频繁的 Cluster nodes 命令
在运维过程当中发现前端请求的平均 RT 增长很多,大概 50% 左右。经过一番调研,发现是频繁的 cluster nodes 命令致使。
当时集群规模为 500+ 节点,cluster nodes 命令返回的结果大小有 103KB。cluster nodes 命令的频率为:每隔 20s 向集群全部节点发送。
提升 Redis 集群吞吐的方法 3. 调优 hz 参数
Redis 会定时作一些任务,任务频率由 hz 参数规定,定时任务主要包含:
主动清除过时数据 对数据库进行渐式Rehash 处理客户端超时 更新请求统计信息 发送集群心跳包 发送主从心跳
如下是做者对于 hz 参数的介绍:
咱们没有修改 hz 参数的经验,因为其复杂性,而且在 hz 默认值 10 的状况下,理论上不会对 Redis 吞吐量产生太大影响,建议没有经验的状况下不要修改该参数。
参考资料
关于 Redis Cluster 能够参考官方的两篇文档:
Redis cluster tutorial: http://www.redis.io/topics/cluster-tutorial Redis Cluster specification: http://www.redis.io/topics/cluster-spec