您当前的位置:首页 > 电脑百科 > 数据库 > Redis

高并发服务遇Redis瓶颈引发的事故

时间:2021-01-07 10:55:13  来源:  作者:

元旦期间 订单业务线 告知 推送系统 无法正常收发消息,作为推送系统维护者的我正外面潇洒,无法第一时间回去,直接让 ops 帮忙重启服务,一切好了起来,重启果然是个大杀器。由于推送系统本身是分布式部署,消息有做各种的可靠性策略,所以重启是不会丢失消息事件的。

:sweat_smile: 事后通过日志分析有大量的 redis 的报错,十分钟内有 16w 次的错误。日志的错误是 connect: cannot assign requested address 。该错误不是推送服务内部及 redis 库返回的 error,而是系统回馈的 errno 错误。

这个错误是由于无法申请可用地址引起的,也就是无法申请到可用的 socket。

话说,元旦当天在线数和订单量确实大了不少,往常推送系统的长连接客户端在 35w,这次峰值飙到 50w 左右, 集群共 6 个节点,其中有 4 个节点每个都扛了 9w+ 的长连接。另外,推送的消息量也随之翻倍。

高并发服务遇Redis瓶颈引发的事故

 

分析

下面是 kibana 日志的统计,出错的时间区间里有近 16w 次的 redis 报错。

高并发服务遇Redis瓶颈引发的事故

 

下面是出问题节点的 TCP 连接状况,可以看到 established 在 6w,而 time-wait 连接干到 2w 多个。

高并发服务遇Redis瓶颈引发的事故

 

为什么会产生这么多 time-wait?谁主动关闭就就有 time-wait,但推送系统除了协议解析失败之外,其余情况都不会主动 close 客户端,哪怕是鉴权失败和弱网络客户端写缓冲爆满,事后通过日志也确定了不是推送系统自身产生的 tw。

另外,linux 主机被 ops 交付时应该有做内核调优初始化的,在开启 tw_reuse 参数后,time-wait 是可以复用的。难道是没开启 reuse?

查看 sysctl.conf 的内核参数得知,果然 tcp_tw_reuse 参数没有打开,不能快速地复用还处在 time-wait 状态的地址,只能等待 time-wait 的超时关闭,rfc 协议里规定等待 2 分钟左右,开启 tw_reuse可在 1s 后复用该地址。另外 ip_local_port_range 端口范围也不大,缩短了可用的连接范围。

sysctl  -a|egrep "tw_reuse|timestamp|local_port"


net.ipv4.ip_local_port_range = 35768    60999net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_tw_reuse = 0

所以,由于没有可用地址才爆出了 connect: cannot assign requested address 错误。

内在问题

追究问题

上面是表象问题,来查查为什么会有这么多的 time-wait ?再说一遍,通常哪一端主动 close fd,哪一端就会产生 time-wait。事后通过 netstat 得知 time-wait 连接基本是来自 redis 主机。

下面是推送代码中的连接池配置,空闲连接池只有 50,最大可以 new 的连接可以到 500 个。这代表当有大量请求时,企图先从 size 为 50 的连接池里获取连接,如果拿不到连接则 new 一个新连接,连接用完了后需要归还连接池,如果这时候连接池已经满了,那么该连接会主动进行 close 关闭。

MaxIdle   = 50MaxActive = 500
Wait      = false

除此之外,还发现一个问题。有几处 redis 的处理逻辑是异步的,比如每次收到心跳包都会 go 一个协程去更新 redis, 这也加剧了连接池的抢夺,改为同步代码。这样在一个连接上下文中同时只对一个 redis 连接操作。

解决方法

调大 golang redis client 的 maxIdle 连接池大小,避免了大并发下无空闲连接而新建连接和池子爆满又不能归还连接的尴尬场面。当 pool wait 为 true 时,意味着如果空闲池中没有可用的连接,且当前已建立连接的连接数大于 MaxActive 最大空闲数,则一直阻塞等待其他人归还连接。反之直接返回 "connection pool exhausted" 错误。

MaxIdle   = 300MaxActive = 400
Wait      = true

redis 的 qps 性能瓶颈

redis 的性能一直是大家所称赞的,在不使用 redis 6.0 multi io thread 下,QPS 一般可以在 13w 左右,如果使用多指令和 pipeline 的话,可以干到 40w 的 OPS 命令数,当然 qps 还是在 12w-13w 左右。

Redis QPS 高低跟 redis 版本和 cpu hz、cache 存在正比关系

根据我的经验,在内网环境下且已实例化连接对象,单条 redis 指令请求耗时通常在 0.2ms 左右,200us 已经够快了,但为什么还会有大量因 redis client 连接池无空闲连接而建立新连接的情况?

通过 grafana 监控分析 redis 集群,发现有几个节点 QPS 已经到了 Redis 单实例性能瓶颈,QPS 干到了近 15w 左右。难怪不能快速处理来自业务的 redis 请求。这个瓶颈必然会影响请求的时延。请求的时延都高了,连接池不能及时返回连接池,所以就造成了文章开头说的问题。总之,业务流量的暴增引起了一系列问题。

高并发服务遇Redis瓶颈引发的事故

 

发现问题,那么就要解决问题,redis 的 qps 优化方案有两步:

• 扩容 redis 节点,迁移 slot 使其分担流量 • 尽量把程序中 redis 的请求改成批量模式

增加节点容易,批量也容易。起初在优化推送系统时,已经把同一个逻辑中的 redis 操作改为批量模式了。但问题来了,很多的 redis 操作在不同的逻辑块里面,没法合成一个 pipeline。

然后做了进一步的优化,把不同逻辑中的 redis 请求合并到一个 pipeline 里,优点在于提高了 redis 的吞吐,减少了 socket 系统调用、网络中断开销,缺点是增加了逻辑复杂度,使用 channal 管道做队列及通知增加了 runtime 调度开销,pipeline worker 触发条件是满足 3 个 command 或 5ms 超时,定时器采用分段的时间轮。

对比优化修改前,cpu开销减少了 3% 左右,redis qps降到 7w 左右,当然概率上消息的时延会高了几个ms。

实现的逻辑参考下图,调用方把redis command和接收结果的chan推送到任务队列中,然后由一个worker去消费,worker组装多个redis cmd为pipeline,向redis发起请求并拿回结果,拆解结果集后,给每个命令对应的结果chan推送结果。调用方在推送任务到队列后,就一直监听传输结果的chan。

高并发服务遇Redis瓶颈引发的事故

 

这个方案来自我在上家公司做推送系统的经验,有兴趣的朋友可以看看 PPT,内涵不少高并发经验。 分布式推送系统设计与实现

总结

推送系统设计之初是预计 15w 的长连接数,稳定后再无优化调整,也一直稳定跑在线上。后面随着业务的暴涨,长连接数也一直跟着暴涨,现在日常稳定在 35w,出问题时暴到 50w,我们没有因为业务暴增进行整条链路压测及优化。

话说,如果对推送系统平时多上点心也不至于出这个问题。我曾经开发过相对高规格的推送系统,而现在公司的推送系统我是后接手的,由于它的架子一般,但业务性又太强,看着脑仁疼,所以就没有推倒来重构。一直是在这个架子上添添补补,做了一些常规的性能优化。嗯,看来不能掉以轻心,免得绩效离我远去。



Tags:Redis瓶颈   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
元旦期间 订单业务线 告知 推送系统 无法正常收发消息,作为推送系统维护者的我正外面潇洒,无法第一时间回去,直接让 ops 帮忙重启服务,一切好了起来,重启果然是个大杀器。由于推...【详细内容】
2021-01-07  Tags: Redis瓶颈  点击:(146)  评论:(0)  加入收藏
▌简易百科推荐
来源: my.oschina.net/xiaomu0082/blog/2990388首先说下问题现象:内网sandbox环境API持续1周出现应用卡死,所有api无响应现象刚开始当测试抱怨环境响应慢的时候 ,我们重启一下应...【详细内容】
2021-12-08  Java识堂    Tags:Redis   点击:(16)  评论:(0)  加入收藏
我不知道为什么你会选择对特定数量的“错误”(或警告)如此具体。听起来您正在寻找将要发布到 Yahoo! 的某些文章的内容。 Insider (N Foos to Blah for the BlahBlah)。那说:...【详细内容】
2021-12-07  富集云科技有限公司    Tags:Redis   点击:(14)  评论:(0)  加入收藏
目录 一、背景 二、步骤 0.理论支持 1、获取数据 2、结果 3、分析数据并评估大小 三、关于repl-backlog-size 一、背景 repl-backlog-size控制这个环形缓冲区. ​ 主从断...【详细内容】
2021-11-05  弈秋的美好生活    Tags:redis   点击:(41)  评论:(0)  加入收藏
Redis 性能测试是通过同时执行多个命令实现的。1,Redis-benchmarkRedis性能命令:redis性能命令格式: redis-benchmark [option] [option value] redis 性能测试工具可选参数如...【详细内容】
2021-11-02  川石信息    Tags:Redis   点击:(41)  评论:(0)  加入收藏
1 概述数据结构和内部编码 无传统关系型数据库的 Table 模型schema 所对应的db仅以编号区分。同一 db 内,key 作为顶层模型,它的值是扁平化的。即 db 就是key的命名空间。 key...【详细内容】
2021-11-01  JavaEdge    Tags:Redis   点击:(28)  评论:(0)  加入收藏
普通java中使用引用Java redis 驱动,即可连接:import redis.clients.jedis.Jedis; public class RedisTestJava { public static void main(String[] args) { //连...【详细内容】
2021-10-13  faesuite    Tags:Redis   点击:(34)  评论:(0)  加入收藏
Redis常用的数据结构有 string list set zset hashstringstring 是 Redis 的基本的数据类型,一个 key 对应一个 value。string 类型是二进制安全的,Redis的string可以包含任...【详细内容】
2021-10-12  语霖    Tags:Redis   点击:(36)  评论:(0)  加入收藏
列表类型可以存储一组按插入顺序排序的字符串,它非常灵活,支持在两端插入、弹出数据,可以充当栈和队列的角色。> LPUSH fruit apple(integer) 1> RPUSH fruit banana(integer)...【详细内容】
2021-09-17  深夜敲代码    Tags:Redis   点击:(54)  评论:(0)  加入收藏
Redis持久化意义 是做灾难恢复,数据恢复,也可以归类到高可用的一个环节里面去,比如你的redis整个挂了,然后redis就不可用了,你要做的事情是让redis变得可用,尽快变得可用 大量的请...【详细内容】
2021-08-12  小李说IT    Tags:Redis   点击:(77)  评论:(0)  加入收藏
当查询Redis中没有的数据时,该查询会下沉到数据库层,同时数据库层也没有该数据,当这种情况大量出现或被恶意攻击时,接口的访问全部透过Redis访问数据库,而数据库中也没有这些数据...【详细内容】
2021-07-30  随便t    Tags:缓存穿透   点击:(90)  评论:(0)  加入收藏
相关文章
    无相关信息
最新更新
栏目热门
栏目头条