一次让性能提升 1500% 的经历

栏目: 数据库 · 发布时间: 6年前

内容简介:一般来说,我这里的基础设施得益于 CDN、OSS 和网关层,到了这里都不用扛太多的流量,只要写的不怎么车祸,后端都不存在什么性能瓶颈。但是这次要上一个灰度功能,需要和业务相结合,在此之前我们的灰度都是以 DNS 为标准进行地区式的灰度,但是现在流量必须要直接打到后端才能判断,如果前面缓存了就没办法灰度了——因此没办法,我们只能船到桥头自然直——扛吧。先来简单描述一下我们的代码逻辑:查询 1 -> 处理逻辑 -> 查询 2 -> 查询 3 -> 处理逻辑 -> 灰度判断 -> 返回。

一般来说,我这里的基础设施得益于 CDN、OSS 和网关层,到了这里都不用扛太多的流量,只要写的不怎么车祸,后端都不存在什么性能瓶颈。

但是这次要上一个灰度功能,需要和业务相结合,在此之前我们的灰度都是以 DNS 为标准进行地区式的灰度,但是现在流量必须要直接打到后端才能判断,如果前面缓存了就没办法灰度了——因此没办法,我们只能船到桥头自然直——扛吧。

先来简单描述一下我们的代码逻辑:查询 1 -> 处理逻辑 -> 查询 2 -> 查询 3 -> 处理逻辑 -> 灰度判断 -> 返回。

最初保持了最原始的代码,没有设置任何缓存,这在过去是满足需求的,因为缓存是由网关层的反向代理帮我们在 Header 里加上的,包括了 s-max-age 和 max-age。

然后开压: wrk -c20 -t20 -d1m

20 threads and 20 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   195.58ms   73.94ms 592.81ms   68.39%
    Req/Sec     5.59      2.83    20.00     63.45%
  6155 requests in 1.00m, 3.94MB read
Requests/sec:    102.40
Transfer/sec:     67.19KB

最初我们以为是压得连接数太少,增大了之后 QPS 不增反降,跟做网关的大佬交流了一下之后知道了以下两点:

  1. QPS 并不是压多少有多少,还要看服务器的处理能力
  2. CPU 和内存都只用了百分之十几,说明瓶颈不在这里,很有可能是 IO

而我们的语句中 IO 的部分只有网络 IO 的数据库操作,于是我们为数据库操作加上了一层 LRU 缓存,大致是这样的:

let result
if (cache(query) is not empty) {
  result = cache(query)
} else {
  result = await db(query)
}

cache.set(result)

当然,这只是一段伪代码。

然后我们再压了一次:

2 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   337.68ms  485.34ms   2.00s    84.04%
    Req/Sec    79.44     31.82   323.00     81.98%
  9237 requests in 1.00m, 5.92MB read
  Socket errors: connect 0, read 0, write 0, timeout 4952
Requests/sec:    153.71
Transfer/sec:    100.92KB

性能提升很有限(实际上上面这段是有问题的,读者朋友先思考一下,之后再说),这个时候压测的同学忍不住了,开始和我一起 Review 代码,我们又开始怀疑了连接池的大小。

我们将连接池的最大连接数扩大了十倍,接着压,提升依旧不明显。

然后我们进行了本地调试,开始打日志之后发现 SQL 语句在有缓存的情况下依旧进行了重复请求,这个时候才回想到因为我们没有合并请求(也不好合并请求,因为 querystring 不同),所以并发的数据进来如果没有处理完,其实此时还没有存到 cache,别的请求会接着查询,直到第一波查询完毕,严重的影响了 QPS。

所以我们把查询的 Promise 缓存,而不是请求的结果。(实际上之前做过这种操作的项目,但是一时没想起来)

let resultPromise
if (cache(query) is not empty) {
  resultPromise = cache(query)
} else {
  resultPromise = db(query)
}

cache.set(resultPromise)
result = await resultPromise

压测:

2 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   206.88ms  305.24ms   2.39s    89.53%
    Req/Sec     1.46k   580.86     2.65k    71.35%
  28138 requests in 10.10s, 18.00MB read
Requests/sec:   2785.71
Transfer/sec:      1.78MB

并且监控中 CPU 和内存占用也比以前更低了(不过当然没有 QPS 明显拉),系统能在同时处理 1500% 的请求了。

当然,为了避免缓存永远被使用,我们这里设置了五分钟的超时缓存,并且仅当没有缓存时才执行 cache.set (因为 set 会刷新缓存时间计数器),也就是说,每五分钟才请求一次,这样的频率相信也是我们的用户(业务方)能接受的范围内。


以上所述就是小编给大家介绍的《一次让性能提升 1500% 的经历》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Python高级编程(第二版)

Python高级编程(第二版)

[波兰] Michał Jaworski、[法] Tarek Ziadé / 张亮、阿信 / 人民邮电出版社 / 2017-9-19 / 89.00元

Python作为一种高级程序设计语言,凭借其简洁、易读及可扩展性日渐成为程序设计领域备受推崇的语言之一。 本书基于Python 3.5版本进行讲解,通过13章的内容,深度揭示了Python编程的高级技巧。本书从Python语言及其社区的现状开始介绍,对Python语法、命名规则、Python包的编写、部署代码、扩展程序开发、管理代码、文档编写、测试开发、代码优化、并发编程、设计模式等重要话题......一起来看看 《Python高级编程(第二版)》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

在线进制转换器
在线进制转换器

各进制数互转换器