美文网首页
基于redis的抢资源应用负载高问题调查

基于redis的抢资源应用负载高问题调查

作者: 哈密朵 | 来源:发表于2019-11-07 23:13 被阅读0次

    自述:这是一个存在10多年的老应用,其本身有很多待解决更新的技术债务。抢资源类似抢红包系统,没有使用缓存也没有限流,即使是使用redis也很糟糕的使用多个慢操作。本文的目的不是想表达解决了什么难度的问题,是想说明面对线上问题,缺乏经验时会对数据不敏感,面对一堆现象无法挑出最核心的线索。我完整记录了我思考错误和之后纠正的过程,给大家引发一些面对问题的思考方式。

    问题现象

    一个历史系统于每天晚上 20:00 开始,放出一批资源,供公司销售同事抢占作为私有资源,高峰期持续2分钟到20:02。

    从调用接口来看,抢资源分为 listAccount、detailAccount、 robAccount。listAccount是列表页查询,简单理解由3个SRANDMEMBER和1个HMGET共4个redis操作组成。查到列表之后,点击进行detailAccount详情查询,并点击进行robAccount抢占。

    从销售人员的用户行为来看,他们会一直按F5刷新查询列表页,所以详情查询和抢占请求会是很少数。

    从nginx日志分析,20:00开始整体响应变慢甚至超过1秒,5分钟内慢记录占全天一半左右,而其中90%以上均为列表页查询,也印证了用户行为的分布状况。nginx日志中列表页80%以上都返回499。而499是Nginx自定义的一个状态码,表示客户端主动关闭连接,无法返回结果。其实用户浏览器对F5无限刷新会进行请求筛选,不会每次刷新都发出新的请求,每个浏览器的策略有轻微差别。但从大量的499来看还是重复发出了不少请求,F5产生的爆炸力还是挺惊人的。

    从应用日志来看,20:00:37秒开始出现大量的JedisConnectionException: Could not get a resource from the pool,配置的timeout是2秒,还有少量的readTimeout异常。从pingpoint监控来看20:00:37开始只看到readTimeout异常没有get resource from pool异常,因此对pingpoint的采集精准度表示质疑。

    从redis实例来看,info命令打印出来的clint连接数一直处于46左右的很低数量,从slow-log得知20:00:37开始有7个1ms的慢操作,均是列表页的SRANDMEMBER和HMGET,到20:02分抢客户高峰期过了之后就不再有slow-log出现。从zabbix来看redis所在虚拟机的cpu只有60%左右,进出IO低,负载系数也不高。

    初步分析

    所以从应用客户端取不到连接且reids只有46连接数和不高的负载来简单判断,可能是客户端连接来不及创建。这里出现了两个错误判断:
    一是从20:00:37到20:02分请求高峰期间,已经出现7个1ms slow-log,1ms看似很快没在意,忽略了redis是单线程架构TPS能达到单机读写2万,明知SRANDMEMBER和HMGET都是需要警惕的慢操作,忘记换算1ms对应的TPS已经降到1000,这已经是很明显的性能警告,前面nginx统计高峰期间应用各实例合计每秒有1300个listAccount即5200次redis读写,远远超过1000TPS所以才出现1ms的slow-log。
    二是没有通过top命令直接查看机器负载而是依赖zabbix,忽略了zabbix采样间隔过大导致可能错过请求高峰期间的精准信息,不是cpu负载不高,只是zabbix跳过去了没采集到。

    问题解决

    基于客户端连接来不及创建的初步结论,我们修改cache-client默认配置,设置idle-connect数量从0改为30-50。再次观察redis实例,info命令显示client连接数维持在280左右的较低数量,说明每个应用都准备好了空闲连接。但现象没有任何区别,20:00:37到20:02分期间,slow-log依然出现,应用日志依然抛出readTimeout异常,pingpoint大量请求响应时间陡增,nginx日志大量慢查询和499。之后才意识到slow-log里1ms表达出来的严重性能问题,直接top命令观察到redis机器已经cpu 97%,说明页面F5发出的大量重复的listAccount请求转化为SRANDMEMBER和HMGET,对redis造成了严重的负担。

    回顾redis的架构,不管是连接事件还是读写事件,都是排队被单线程执行。如果有慢操作阻塞,其余读写事件很可能会readTimeout,连接事件很容易超过2秒抛出Connection异常。就算提前准备好空闲连接,只要阻塞的慢操作还在,整体效果不会有改观。

    最后使用guava cache建立应用本地缓存,相邻1s的listAccount结果会被缓存并直接返回,就这一步就省了大量的SRANDMEMBER和HMGET慢操作,redis的负担瞬间就没了。使用guava cache优化后改善效果明显,从nginx日志来看,虽然列表页还有499记录(由于运维日志没有时间记录,无法知道是否超时),但499本就由用户主动触发可视为没有影响,对于有时间记录的列表页查询慢记录,3秒以上的由6000次降为0,1-3秒的由1800此降为180。

    思考:

    考虑采用限流或缓存方式改造系统。

    相关文章

      网友评论

          本文标题:基于redis的抢资源应用负载高问题调查

          本文链接:https://www.haomeiwen.com/subject/lppbbctx.html