天天看点

饿了么Redis Cluster集群化演进

2017运维/devops在线技术峰会上,饿了么运维负责人程炎岭带来题为“饿了么redis cluster集群化演进”的演讲。本文主要从数据和背景开始谈起,并对redis的治理进行分析,接着分享了redis cluster的优缺点,重点分析了corvus,包括commands、逻辑架构和物理部署等,最后分享了redis的运维和开发,并作了简要总结,一起来瞧瞧吧。

以下是精彩内容整理:

近几个月,运维事件频发。从“炉石数据被删”到“mongodb遭黑客勒索”,从“gitlab数据库被误删”到某家公司漏洞被组合攻击。这些事件,无一不在呐喊——做好运维工作的重要性。然而,从传统it部署到云,人肉运维已经是过去式,云上运维该怎么开展?尤其是云2.0时代,运维已经向全局化、流程化和精细化模式转变。与此同时,人工智能的发展,“威胁论”也随之袭来——运维是不是快要无用武之地了?如何去做更智能的活,当下很多运维人在不断思考和探寻答案。

redis cluster在业内有唯品会、优酷蓝鲸等很多公司都已经在线上有了一定规模的redis cluster集群,那么,饿了么是怎样从现有的redis技术迁移到redis cluster集群上的呢?

背景和数据

2015-2016年是饿了么的业务爆炸式增长阶段,订单量从几十w到到峰值900w/天,在增长的过程中我们也遇到了一些问题,我们有服务化治理诉求、应用性能和稳定诉求、人肉运维成本诉求,并且还要为未来的paas平台池化准备。

饿了么Redis Cluster集群化演进

横坐标是订单量,纵坐标是集群数量,在多机房下整个集群规模是double的,可以看到,千万订单的集群规模大概在200个,在一个机房内我们有近200套集群,近4t数据在redis里面,整个节点数大约在10000个左右。

饿了么Redis Cluster集群化演进

上图表示在高峰期时每天不同订单量下,整个redis集群承载ops的需求。

<b>使用场景</b><b></b>

外卖行业的使用场景要结合业务来看,所有需要高性能、高并发、降级数据库场景下都用到了redis做缓存,包括用户端、商户端和物流配送,搜索排序、热卖、画像等目前也是往redis里推送,除了缓存外,还有各类cache、计数器、分布式锁等。

为什么会考虑向redis cluster演进呢?

因为redis存在很多问题,比如单点的内存上限受限制,将redis当成db或cache混用,共用一个redis、大key阻塞整个实例,经常需要扩容、扩容很痛苦,使用复杂且基础环境非标、存在各种配置,监控缺失以及不能统一配置等诸多问题。

治理

由于redis请求量太大,打满了机器网卡,所以在2016年5月启动了治理redis的项目。

在自研选型部分,在ops推广治理时我们与业内成熟技术做了比较,包括redis vs redis cluster,以及twenproxy vs codis vs redis cluster,比较了单节点的redis和redis

cluster,还有业内经过生产考验的proxy,最后决定自研自己的redis cluster + proxy。

redis cluster 的优缺点

为什么选择redis cluster作为我们的底层存储呢?

redis cluster自带迁移功能,性能快、高可用,支持在线分片,丰富集群管理命令等。自研proxy在初期快速在生产上应用时候可以省略掉这部分,让redis  cluster本身给我们提供这项功能就足够了。

redis cluster也有一些不足:

client 实现复杂,需要缓存slot mapping关系并及时更新;

client不成熟导致提高开发难度;

存储和分布式逻辑耦合,节点太多时节点之间的检测占大量网卡带宽;

3.0.6版本前,只能单个key迁移,并且同时只允许一个slot处于迁移状态。

corvus

饿了么Redis Cluster集群化演进

图中为corvus时序设计,可以看到,actor到后面的redis,中间的proxy、client和server是在corvus中实现的逻辑,proxy处理connect操作,接收用户的请求发送到client,server最终实现命令的处理。我们分成三个分类,对于单key、特殊命令和多key的处理,对复合命令操作做了单独操作,并对返回结果做聚合,同时还有一些特殊命令,比如cluster命令的处理逻辑。

corvus封装了redis cluster 协议,提供redis 协议,这样用原来成熟的redis

client操作redis cluster集群,接入到corvus,这时可以做到无缝的改动;扩容缩容应用无感知,corvus服务是注册的机制,对应用程序来说,连接是通过一层本地proxy,我们对应用程序提供sdk,但目前我们通过本地proxy连接接入corvus已经能够满足大部分应用;corvus实时缓存了slots mapping,它会不停的发送corvus notes去获取redis node与slots mapping的关系,并更新到corvus本身的缓存中,还有multiple thread、lightweight和reuseport support等。

<b>corvus commands</b>

corvus支持pipeline;

modified commands,对复合命令做spring操作,我们已经对命令本身进行了修改,但可能对我们看监控数据有疑惑,统计端节点命中率与应用程序命中率可能不一样;

restricted commands;

unsupported commands,比如对gu的处理,目前还没有在redis cluster中使用gu的应用,暂时不会支持。

<b>corvus performance</b>

饿了么Redis Cluster集群化演进

图中为corvus的测试报告,这是很极端的压测情况,set/get可以达到140万qps数据。

<b>corvus </b><b>逻辑架构</b><b></b>

饿了么Redis Cluster集群化演进

可以看到,底层存储用redis cluster,web console通过后端ruskit操作redis cluster,当我们注册redis cluster时,appid存储在storage里,监控部分我们通过esm agent发送到grafana,corvus本身也会采集server log发送到statsd去收集,corvus agent去部署时首先要初始化corvus agent注册到host console里,通过corvus agent去拉起或回收某一套redis cluster,huskar是我们的集中配置管理,对应到我们后端的zk技术。

<b>corvus </b><b>物理部署</b><b></b>

饿了么Redis Cluster集群化演进

corvus的物理部署如图所示,上面是应用程序端,应用程序连接到本地的corvus服务端口,corvus服务端口作为本地的sam port,一个corvus实例对应到redis cluster的一些master节点,会部署在不同的物理服务器上,根据不同的和数去创建corvus服务实例以及redis节点。

这里有两层proxy,首先连接本地的sam,也就是go proxy,go proxy这层提供应用服务化、本地接入,corvus是redis cluster proxy这一层,两层proxy意味着两层的性能损耗,在这个基础上,我们也做了针对redis cluster相同的直连的性能损失,对比来看,proxy层性能损耗相比运维成本以及服务化成本而言,是可以接受的。

redis 运维和开发

<b>redis</b><b>运维</b><b></b>

我们将redis移交数据库团队运维

统一redis 物理部署架构

标准化redis/corvus

服务器以及单机容量

设定redis 开发设计规范

设定redis资源申请流程,做好容量评估

完善监控,监控分corvus和redis

cluster两部分,跟进全链路压测,从最前端打进来,到底层的redis和数据库

根据压测结果做隔离关键集群,比如整个外卖后端和商户的redis,将它们做单独隔离,redis cluster本身对网卡要求很高,分专用资源和公共资源,专用资源的corvus或redis cluster机器独栈给到appid,公共资源是混布情况,我们只需监控网卡流量、cpu等

升级万兆网卡,新采购的服务器都是万兆配置

<b>redis </b><b>开发约定</b><b></b>

redis开发约定如下:

key 格式约定:object-type:id:field。用":"分隔域,用"."作为单词间的连接,对于整个field怎样分隔,我们有很多种改法;

禁止大k-v,我们基于全链路压测,比如一套集群有多种类型的pattern的key,其中某一个的key value很长,占用内存很大,我们就要告诉开发者像这种使用姿势是不对的,须被禁止;

只用作cache, key

ttl设置;

做好容量评估,由于redis

cluster的迁移限制,所以前期我们要做好容量评估,比如整套集群申请100g,我们需要计算需要的内存容量以及所在的应用场景负载;

设定集群上限,规定redis

cluster集群数不超过200,单个节点容量4~8g;

接入统一为本地goproxy

<b>redis </b><b>改造</b><b></b>

我们要定义一个节奏,对redis进行改造,具体如下:

1.        

将redis 迁移cluster

2.        

完善system、redis、corvus监控

3.        

对线上redis拆分、扩容

4.        

非标机器腾挪下线

5.        

大key、热节点、大节点扫描,比如同一个redis cluster集群某个节点明显高于其它节点,我们会做节点分析和key的分析等

6.        

网卡bond,多队列绑定

7.        

修改jedis源码,加入matrix埋点上报etrace,就可以在etrace看到每个应用trace到整条链路到哪一个端口,它的响应时间、请求频次、命中率等。

经验总结

对于redis我们可谓经验丰富,总结如下:

redis 更适合cache,cluster下更不合适做持久化存储

一致性保证,不做读写分离(尽管corvus支持)

单实例不宜过大,节点槽位要均衡

发现问题,复盘,积累经验

不跨机房部署,性能优先

异地多活通过订阅消息更新缓存

大部分的故障都是由于hot-key/big-key

关心流量,必要时立刻升级万兆

<b>遗留问题</b><b></b>

目前redis还不够自动化,某些操作还是分成多个步骤来实现,大集群reshard扩容慢;遇到版本升级问题时,可以新起一条集群;sam-&gt;corvus-&gt;redis链路分析,应用程序在etrace上访问端口会很慢,需要从redis和corvus层层自证等;多活跨机房写缓存同步;我们通过定时任务的发起aof/rdb来保证数据尽快恢复,实际上我们是关闭掉的;冷数据也是不够完善的,还有集群自愈,当遇到问题时我们可以快速拉起另外一套集群将数据更新过去,或者通过数据更新预热将缓存更新过去。

<b>池化</b><b></b>

我们在根据全链路压测时的一些特性或数据去做集群隔离,分放公共池和专用池,以及对整个池内资源做buffer的监控;资源申请只需关注容量以及使用方式,无需关心机器;另外,新申请以及扩容缩容按组(group)操作,运维操作自动化,不需要登录到某个机器上执行某个命令。

<b>开发自助</b><b></b>

我们最终的目标是在缓存部分实现开发自助,具体步骤如下:

开发自助申请redis 资源

填入demo key/value,自动计算容量

状态,告警推送,对线上实时运行情况的透明化

各类文档齐全

目前我们还做不到,需要ops团队干预开发。

继续阅读