1. 云栖社区>
  2. 博客列表>
  3. 正文

一步一个脚印:解密唯品会中Redis集群架构演进与功能定制

云学习小组 2016-11-01 17:13:06 浏览2112 评论0

redis 杭州云栖大会 唯品会 Twemproxy集群 Sharding架构

摘要: 在2016杭州云栖大会的“开源数据库之Redis专场”上,来自唯品会的高级数据工程师申政带来了题为《Redis在唯品会的应用实践》的精彩分享。分享中,他主要介绍Redis集群架构演进、Redis使用经验以及唯品会对Redis二次开发实践积累三部分,干货满满,精彩不容错过。

在2016杭州云栖大会的“开源数据库之Redis专场”上,来自唯品会的高级数据工程师申政带来了题为《Redis在唯品会的应用实践》的精彩分享。分享中,他主要介绍Redis集群架构演进、Redis使用经验以及唯品会对Redis二次开发实践积累三部分,干货满满,精彩不容错过。

以下内容根据演讲PPT及现场分享整理。


Redis集群架构演进

目前在唯品会内对Redis的使用属于重量级别,目前在唯品会内大概有8000个Redis实例、1000台物理机、500个应用。

ac5f135676f37a118e26fdb366e39b687e02b8e9

上图是唯品会对Redis使用的演进历史图。在2014年7月左右,采用的是客户端分片(Client Sharding)的架构;2014年7月到2015年12月期间,主要采用的是Twemproxy集群模式;从2015年6月至今,唯品会内主要采用Redis Cluster框架。

下面简单介绍上述三种框架的优缺点。

Client Sharding架构

9130e5cd0b9cb2ec393426c003b39088df42c2ca

Client Shareding的架构如上图所示,从图上可以看出该架构十分简单稳定,但它需要业务方面自定义Key分布,进而增加了业务开发难度和工作量;其次该架构的在线扩容能力非常弱,并且不能在数据库层面提供Failover能力。

Twemproxy架构

1eeba89c40e45d3cad1b0223df02e72574e3c186

唯品会内对Twemproxy的使用超过2000个实例、800台物理机、150个应用,其框架如上图所示,相比于Client Shareding的架构复杂了很多,通过在Twempoxy层上增加了LVS,既增加了Twempoxy的高可用,又可以在LVS下任意增加或删除Twempoxy节点,很好的支持了增量业务。

Twemproxy负责数据分片,下面可以挂载多个Redis节点,此外架构中通过采用Redis官方提供的HA保证主从之间的高可用。Twemproxy具有很多优点:

  1. 支持数据自分片和一致性Hash
  2. 同时支持RedisMC协议
  3. 支持pipelinemgetmset等多Key操作
  4. Twemproxy自身扩容简单,可以做到用户无感知,降低了业务开发复杂度

事有正反面,在使用Twemproxy过程中也发现其存在很多不足:首先该架构十分复杂,导致机器成本高;其次,Redis和MC扩容比较难;此外,该架构分为三层,每一个环节都可能成为集群的性能瓶颈;客户端的请求需要经过三层的请求链路,导致请求相应时间长;最后尤其值得注意的一点是:它的运维成本很高。

Redis Cluster

adabc412cf3cc1e54c7df768fefa7f91a58302e0

Redis Cluster框架如上图所示,从图中可以看出:它是一个无中心框架,集群中每个节点完全对等,任一节点都与其他节点保持一个长连接,通过高速协议维护整个集群的状态信息;并且该架构还提供了自动Failover和在线扩缩容能力;同时采用单层框架,相应时间短;此外,由于该架构中客户端与Redis直接相连,相比于Twemproxy架构,节省了一半的机器开销。

但该架构对客户端依赖非常重,需要智能客户端支持;对Mget、Mset、Pipeline支持不友好。

一些经验

下面分享一下在使用Redis过程中踩过的坑,供大家借鉴。

f01cd70dd9f0e516d586027c86673a131e6036a3

在使用Twemproxy时,常发现线上同目录实例的内存特别大,达到几G、几十G,甚至比Redis占据的内存还要大。这是因为在业务开发时,一次使用Pipeline过大,命令数量特别多,这是由于Twemproxy是一次性接受完客户端发来的所有数据再转发导致;同时Twemproxy申请的内存申请后不释放,进而导致了内存只增不减。

因此,建议在使用Twemproxy时,一次Pipeline包含的命令量适中,最好不要超过1000个。

ac90c8d9040c8479e5f99a4c0e6a05d37bf90744

按照Twemproxy执行Hash规则,当后端server出现问题时,理论上只会影响当前Server内的Key。但我们遇到的问题是:当其中的一个Server出现问题时,其他Server返回请求都出现问题。这是由Twemproxy代码结构所决定的,它对每一个客户端的链接都有一个队列,当队列头部的请求恰好是出现问题Server的请求(不能响应到Twemproxy),则该请求会将队列中后续的其他Server请求全部卡死,此时这些请求都缓存在Twemproxy,不能相应到客户端,进而导致Twemproxy的内存飙升。

还有一点需要注意:Twemproxy中Timeout参数,默认是永远等待,即便Server没用相应,但其仍认为该Server是正常的,一直等待下去。因此在使用Twemproxy时,务必要注意Timeout参数的设置。

b0966ed7566afdcbaf3b4647de3bdf63aa0d8029

Twemproxy中设计了剔除策略。有时Twemproxy下面挂载的Redis/MC并没有挂掉,网络也没出现问题,但Twemproxy会剔除某些Server。这是由Twemprox自身的设计缺陷导致,在Twemproxy中存在server_connections和server_failure_limit两个参数,前者表示Twemproxy与后端Server建立长连接的数量;后者是Twemproxy与后端server出现问题的连接数,如果连接连续出现问题,则触发剔除策略。

在实际使用时,建议设置server_connections数目小于server_failure_limit,以规避剔除现象的发生。

416ca2d5df964361d451af1f7c64646bf899a469

在使用Twemproxy时,曾出现过这种情况:业务监控客户端连接只有几十个,而Twemproxy端连接却有几千个、甚至上万个。

这是由LVS机制导致的:LVS内有一个专门维护客户端向后端发起连接数量的表,其内置了exprie机制,当连接在15min没有活跃的请求时,会将该连接从表内剔除,而不通知双方。此时,Twemproxy认为该连接是正常的,而客户端发现连接不通时,会新建连接继续发送请求,长此以往,两者的连接数差距越来越大;并且Twemproxy的文件描述符会消耗很快,直到不能再正常建立连接。

为了解决该问题,在Twemproxy中增加了TCPKeepalive功能,完美地规避了该情况的发生。

 

37416899a9f477866a9d30a70ccaab1e991a303c

如果在使用Twemproxy时,发现线上Twemproxy内存监控曲线如上图所示,并且在业务中还用到了Mset命令,基本上可以确定是由Mset引起的内存泄露。

在Mset命令进行拆分时,由于特殊情景导致内存不被释放,进而导致Twemproxy内存越来越大。应对该问题,可以采用重启Twemproxy进行解决。

在使用Redis Cluster的过程中,唯品会积累了一些实践技巧,先总结如下:

  •  Cluster-require-full-coverage建议为no(官方默认为yes)
  •  Cluster-node-timeout建议适当增大
  •  jedisCluster注意捕获异常MaxRedirectionsException(该异常表示命令执行不成功)
  •  不建议使用mget、mset等multi-key命令
  •  尽量使用官方redis-trib脚本管理集群,不要轻易使用Cluster命令

二次开发

在使用Redis过程中,为了更加满足业务需求,唯品会对其进行了二次开发。

de641c00e05d1c9c27218ac63f24aa43a051aecb

对于Twemproxy,为了保障其高可用性,增加了replace_server命令,当Redis Master挂掉之后,Sentinel调用脚本通过replace_server命令把Twemproxy中的Master替换成Slave。

 

d41ae42b8e8819e3467426d13b867095d318c57a

目前唯品会线上最大的Twemproxy集群用到的实例超过100个,如果想更改Twemproxy配置时,工作量是比较大的。因此,唯品会在Twemproxy集群中集成zookeeper,Twemproxy集群的配置信息统一由Zookeeper管理、分发,用户无感知。

7b00f3dbdd75ed4dc66eb1c2c1b9e3d68045bb72

唯品会对MC也进行可二次开发,由于MC中不存在主从概念,但MV集群中其中一个节点挂掉之后会对后端的数据库造成很大的压力。为了解决该问题,在Twemproxy层面增加了复制池(Replication pool)功能,在Master pool和Slave pool之间建立主从关系,客户端同时写入两个pool内;读取时,首先读取Master pool中内容,当Master pool中无数据时,并不会回传到数据库,而是从Slave pool中获取数据,有效减轻由于MC挂掉带给后端数据库的压力。

5ca14f3eeba46123bc54f4936b91a64204afd701

借鉴Redis,在Twemproxy中增加了Slowlog功能,停留时间;此外,还增加简单统计信息功能,将一天分为几个时间段,每个时间段记录Slowlog次数,便于定位Slowlog和解决Slowlog。

5989cc4624654a74592b33027b19acb6e76b2382

增加多线程功能是对Twemproxy一次大的改动。Twemproxy提供的QPS在几万左右,由于线上每个物理机上只能部署一个Twemproxy,导致了物理机极大地浪费。借鉴MC多线程模型,建立了master线程+N个worker线程多线程模型:master线程负责监听端口,接受新的客户端连接;worker线程负责处理客户端请求。Twemproxy多线程开6个线程时,QPS可以达到35万/s,同时也节约了千万服务器成本。

24c6e2cb35f47e6255af217b3364285d26ad5e5f

很多用户在使用Redis Cluster是都会遇到这样问题:flushall删不掉数据;明明Master是存活的,网络也正常,但Cluster却执行了failover。这是因为Redis是单线程的,在执行时间复杂度为O(n)命令时,线程被阻塞,无法响应其他节点的ping命令,造成假死现象,进而导致主从切换。

针对该问题的处理方式是:增加了额外进程监听额外端口(extra-port),当超过cluster-node-timeout/2s时间,ping请求没有收到回复,主动向extra-port发送ping请求。

4de373083adff8a7b9db3fc9760ed44b6428c9d2

最初上线Redis cluster时,很多客户端并不是友善支持,因此增加了Redis Cluster客户端功能,在Hiredis上进行功能改造使其支持集群模式,用于:

  •  解析并更新路由表
  •  处理moved/ask错误
  •  内含Max redirect机制
  •  支持mget、mset、pipeline、异步API

8020c304170a7b009eab361a302367e726e0cb6a

为了应对Redis集群之间的数据迁移,唯品会开发了Redis Migrate迁移工具,它是基于Redis复制功能的,在迁移过程中,老集群正常对外提供服务;并且支持多线程异构迁移,支持Twemproxy、Redis Cluster、RDB文件和AOF文件恢复;同时迁移过程也可以实时查看;此外,该迁移工具还具有比较完善的数据抽样校验机制,保障迁移数据的一致性。

da2a33785b5631e0b8b19b0aab6b7870d735ed89

Redis Migrate具体迁移步骤如上所示,这其中关键点是左侧的配置文件,这里不不再一一叙述。

300f6c5c8d2863d3b4d85d4e5cf38be5f180c631

Reids多线程是目前唯品会正在改造,借鉴于上文提到的Twemproxy多线程改造,将Redis改造成Master+N个worker多线程模型:客户端有worker线程管理;多线程中不可能避免有锁的存在,Redis中存在DB级别读写锁,通过单独后台线程处理过期key和dict维护。

目前Redis多线程实现支持五大数据结构(String/list/hash/set/zset);支持100个Redis命令(包括管理命令)。

398fdd57fcd40c0314e1a88f15ef067169523599

Redis多线程中,为了降低DB锁竞争,引入了逻辑DB。一个逻辑DB包含N个真实物理DB,用户使用的是逻辑DB。一个逻辑DB的所有Key分散在各个物理DB上吗,每个物理DB拥有一把读写锁——pthread_rwlock_t。

7797f7ac18a774ed376d836a16bb654796a67b88

上图是Redis处理客户端请求的命令过程,这里不再一一叙述。每个Worker线程都会进行上图六个步骤,DB锁的竞争主要是在第四步和第五步上,其他步骤Worker之间完全并行。

Redis多线程-性能测试

abc7b2d2cf9e6a9203b335cdc826978a946d20e5

上图是用于Redis多线程性能测试的测试环境配置。

701aae11ee987a1785bc3e85d0e15162b523e802

测试用例图上图所示,从图上可以看出SET的效率为34万左右;GET效率为40万左右。

ce87b1f542640e0d5eeb0f800f21ea02c854373b

热Key问题是使用Redis中不可避免的问题。在Redis多线程中,遇到热Key首先将其拆分,将其分散到不同的Redis实例中。上图是多线程中针对热Key的性能测试:只写一个key时,四个线程的QPS可以达到30万/s;只读一个Key时,四个线程的QPS可以达到43万/s,一定程度上有效改善了热Key问题。

附录:

文章中提到的唯品会对Redis二次开发的源代码开源地址:http://github.com/vipshop

 

版权声明:本文内容由互联网用户自发贡献,本社区不拥有所有权,也不承担相关法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件至:yqgroup@service.aliyun.com 进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容。

用云栖社区APP,舒服~

【云栖快讯】2017互联网超级工程阿里双11完美落幕,交易额突破1682亿,但阿里工程师如何玩转“超级工程”,背后黑科技又是如何?12月13-14日,12位大咖直播分享揭秘1682亿背后技术实践,马上预约  详情请点击

网友评论

关注
云学习小组
一群热爱技术的朋友们在云栖社区组成的学习小组!
164篇文章|411关注

一种稳定可靠、性能卓越、可弹性伸缩的数据库服务。基于飞天分布式系统和全SSD盘高性能存储,支持主备版和集群版两套... 更多>

为您提供简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本... 更多>
社区之星年度评选,投票可抽奖

社区之星年度评选,投票可抽奖