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

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: 在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

 

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
框架源码私享笔记(01)Tomcat核心架构功能 | 配置详解
本文首先分享了《活出意义来》一书序言中的感悟,强调成功如同幸福,不是刻意追求就能得到,而是全心投入时的副产品。接着探讨了Tomcat的核心功能与架构解析,包括网络连接器(Connector)和Servlet容器(Container),并介绍了其处理HTTP请求的工作流程。文章还详细解释了Tomcat的server.xml配置文件,涵盖了从顶级容器Server到子组件Connector、Engine、Host、Context等的配置参数及作用,帮助读者理解Tomcat的内部机制和配置方法。
大数据新视界--大数据大厂之MySQL数据库课程设计:MySQL集群架构负载均衡故障排除与解决方案
本文深入探讨 MySQL 集群架构负载均衡的常见故障及排除方法。涵盖请求分配不均、节点无法响应、负载均衡器故障等现象,介绍多种负载均衡算法及故障排除步骤,包括检查负载均衡器状态、调整算法、诊断修复节点故障等。还阐述了预防措施与确保系统稳定性的方法,如定期监控维护、备份恢复策略、团队协作与知识管理等。为确保 MySQL 数据库系统高可用性提供全面指导。
阿里面试:Redis 为啥那么快?怎么实现的100W并发?说出了6大架构,面试官跪地: 纯内存 + 尖端结构 + 无锁架构 + EDA架构 + 异步日志 + 集群架构
阿里面试:Redis 为啥那么快?怎么实现的100W并发?说出了6大架构,面试官跪地: 纯内存 + 尖端结构 + 无锁架构 + EDA架构 + 异步日志 + 集群架构
阿里面试:Redis 为啥那么快?怎么实现的100W并发?说出了6大架构,面试官跪地: 纯内存 + 尖端结构 +  无锁架构 +  EDA架构  + 异步日志 + 集群架构
融合AMD与NVIDIA GPU集群的MLOps:异构计算环境中的分布式训练架构实践
本文探讨了如何通过技术手段混合使用AMD与NVIDIA GPU集群以支持PyTorch分布式训练。面对CUDA与ROCm框架互操作性不足的问题,文章提出利用UCC和UCX等统一通信框架实现高效数据传输,并在异构Kubernetes集群中部署任务。通过解决轻度与强度异构环境下的挑战,如计算能力不平衡、内存容量差异及通信性能优化,文章展示了如何无需重构代码即可充分利用异构硬件资源。尽管存在RDMA验证不足、通信性能次优等局限性,但该方案为最大化GPU资源利用率、降低供应商锁定提供了可行路径。源代码已公开,供读者参考实践。
87 3
融合AMD与NVIDIA GPU集群的MLOps:异构计算环境中的分布式训练架构实践
中小医院云HIS系统源码,系统融合HIS与EMR功能,采用B/S架构与SaaS模式,快速交付并简化运维
这是一套专为中小医院和乡镇卫生院设计的云HIS系统源码,基于云端部署,采用B/S架构与SaaS模式,快速交付并简化运维。系统融合HIS与EMR功能,涵盖门诊挂号、预约管理、一体化电子病历、医生护士工作站、收费财务、药品进销存及统计分析等模块。技术栈包括前端Angular+Nginx,后端Java+Spring系列框架,数据库使用MySQL+MyCat。该系统实现患者管理、医嘱处理、费用结算、药品管控等核心业务全流程数字化,助力医疗机构提升效率和服务质量。
地铁站内导航系统解决方案:技术架构与核心功能设计解析
本文旨在分享一套地铁站内导航系统技术方案,通过蓝牙Beacon技术与AI算法的结合,解决传统导航定位不准确、路径规划不合理等问题,提升乘客出行体验,同时为地铁运营商提供数据支持与增值服务。 如需获取校地铁站内智能导航系统方案文档可前往文章最下方获取,如有项目合作及技术交流欢迎私信我们哦~
104 1
CPU的定义与功能与架构
CPU(中央处理器)是计算机的核心部件,负责执行程序指令、控制数据传输和进行运算。它能处理算术与逻辑运算,并协调其他硬件协同工作。x86架构源于英特尔,适用于PC和服务器,采用复杂指令集;ARM架构则由Acorn等公司开发,广泛用于移动设备和嵌入式系统,采用精简指令集,功耗低且能效比高。
133 5
【赵渝强老师】达梦数据库MPP集群的架构
达梦数据库提供大规模并行处理(MPP)架构,以低成本实现高性能并行计算,满足海量数据存储和复杂查询需求。DM MPP采用完全对等无共享体系,消除主节点瓶颈,通过多节点并行执行提升性能。其执行流程包括主EP生成计划、分发任务、各EP并行处理及结果汇总返回。为确保高可用性,建议结合数据守护部署。
Redis 功能扩展 Lua 脚本 对Redis扩展 eval redis.call redis.pcall
通过本文的介绍,我们详细讲解了 Lua 脚本在 Redis 中的作用、`eval` 命令的使用方法以及 `redis.call` 和 `redis.pcall` 的区别和用法。通过合理使用 Lua 脚本,可以实现复杂的业务逻辑,确保操作的原子性,并减少网络开销,从而提高系统的性能和可靠性。
118 13
基于Redis海量数据场景分布式ID架构实践
【11月更文挑战第30天】在现代分布式系统中,生成全局唯一的ID是一个常见且重要的需求。在微服务架构中,各个服务可能需要生成唯一标识符,如用户ID、订单ID等。传统的自增ID已经无法满足在集群环境下保持唯一性的要求,而分布式ID解决方案能够确保即使在多个实例间也能生成全局唯一的标识符。本文将深入探讨如何利用Redis实现分布式ID生成,并通过Java语言展示多个示例,同时分析每个实践方案的优缺点。
166 8

热门文章

最新文章

下一篇
oss创建bucket
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等