按:对于业务技术而言,技术是什么?深刻理解业务的本质,掌握技术底层原理、并合理应用。中间件就是其中支点,作为中间件一员的Redis产品,是如何演进的?与业务系统有何不同?
本文来自知乎陈鹏老师的精彩分享,作者是该系统的负责人,文章深入介绍了知乎Redis系统的方方面面,作为后端程序员值得仔细研究。
背景
知乎作为知名中文知识内容平台,每日处理的访问量巨大,如何更好的承载这样巨大的访问量,同时提供稳定低时延的服务保证,是知乎技术平台同学需要面对的一大挑战。
知乎存储平台团队基于开源Redis 组件打造的 Redis 平台管理系统,经过不断的研发迭代,目前已经形成了一整套完整自动化运维服务体系,提供一键部署集群,一键自动扩缩容, Redis 超细粒度监控,旁路流量分析等辅助功能。
目前,Redis 在知乎规模如下:
● 机器内存总量约70TB,实际使用内存约40TB;
● 平均每秒处理约1500万次请求,峰值每秒约2000万次请求;
● 每天处理约1万亿余次请求;
● 单集群每秒处理最高每秒约400万次请求;
● 集群实例与单机实例总共约800个;
● 实际运行约16000个Redis 实例;
● Redis 使用官方3.0.7版本,少部分实例采用4.0.11版本。
Redis at Zhihu
根据业务的需求,我们将实例区分为单机(Standalone)和集群(Cluster)两种类型,单机实例通常用于容量与性能要求不高的小型存储,而集群则用来应对对性能和容量要求较高的场景。
单机(Standalone)
对于单机实例,我们采用原生主从(Master-Slave)模式实现高可用,常规模式下对外仅暴露 Master 节点。由于使用原生 Redis,所以单机实例支持所有 Redis 指令。
对于单机实例,我们使用Redis 自带的哨兵(Sentinel)集群对实例进行状态监控与 Failover。Sentinel 是 Redis 自带的高可用组件,将 Redis 注册到由多个 Sentinel 组成的 Sentinel 集群后,Sentinel 会对 Redis 实例进行健康检查,当 Redis 发生故障后,Sentinel 会通过 Gossip 协议进行故障检测,确认宕机后会通过一个简化的 Raft 协议来提升 Slave 成为新的 Master。
通常情况我们仅使用1 个 Slave 节点进行冷备,如果有读写分离请求,可以建立多个Read only slave 来进行读写分离。
如图所示,通过向Sentinel 集群注册 Master 节点实现实例的高可用,当提交 Master 实例的连接信息后,Sentinel 会主动探测所有的 Slave 实例并建立连接,定期检查健康状态。客户端通过多种资源发现策略如简单的 DNS 发现 Master 节点,将来有计划迁移到如 Consul 或 etcd 等资源发现组件 。
当Master 节点发生宕机时,Sentinel 集群会提升 Slave 节点为新的 Master,同时在自身的 pubsub channel +switch-master 广播切换的消息,具体消息格式为:
switch-master <master name> <oldip> <oldport> <newip> <newport>
watcher 监听到消息后,会去主动更新资源发现策略,将客户端连接指向新的 Master 节点,完成 Failover,具体 Failover 切换过程详见 Redis 官方文档。
Redis Sentinel Documentation [1]
实际使用中需要注意以下几点:
● 只读Slave 节点可以按照需求设置 slave-priority 参数为0,防止故障切换时选择了只读节点而不是热备 Slave 节点;
● Sentinel 进行故障切换后会执行 CONFIG REWRITE 命令将SLAVEOF 配置落地,如果 Redis 配置中禁用了 CONFIG 命令,切换时会发生错误,可以通过修改 Sentinel 代码来替换 CONFIG 命令;
● Sentinel Group 监控的节点不宜过多,实测超过 500 个切换过程偶尔会进入 TILT 模式,导致Sentinel 工作不正常,推荐部署多个 Sentinel 集群并保证每个集群监控的实例数量小于 300 个;
● Master 节点应与 Slave 节点跨机器部署,有能力的使用方可以跨机架部署,不推荐跨机房部署 Redis 主从实例;
● Sentinel 切换功能主要依赖 down-after-milliseconds 和failover-timeout 两个参数,down-after-milliseconds 决定了Sentinel 判断 Redis 节点宕机的超时,知乎使用 30000 作为阈值。而 failover-timeout 则决定了两次切换之间的最短等待时间,如果对于切换成功率要求较高,可以适当缩短failover-timeout 到秒级保证切换成功,具体详见Redis 官方文档[2];
● 单机网络故障等同于机器宕机,但如果机房全网发生大规模故障会造成主从多次切换,此时资源发现服务可能更新不够及时,需要人工介入。
集群(Cluster)
当实例需要的容量超过20G 或要求的吞吐量超过 20万请求每秒时,我们会使用集群(Cluster)实例来承担流量。集群是通过中间件(客户端或中间代理等)将流量分散到多个 Redis 实例上的解决方案。
知乎的Redis 集群方案经历了两个阶段:客户端分片与 Twemproxy 代理
客户端分片(before 2015)
早期知乎使用redis-shard 进行客户端分片,redis-shard 库内部实现了 CRC32、MD5、SHA1三种哈希算法,支持绝大部分Redis 命令。使用者只需把 redis-shard 当成原生客户端使用即可,无需关注底层分片。