【问题标题】:Our Server can't handle more than 20 requests/second我们的服务器不能处理超过 20 个请求/秒
【发布时间】:2015-11-24 07:17:06
【问题描述】:

因此,经过 3 个月的努力开发和将公司 API 从 PHP 切换到 Go,我发现我们的 Go 服务器不能处理超过 20 个请求/秒。

我们的 API 基本上是如何工作的:

  • 接受请求
  • 验证请求
  • 使用 MYSQL 从数据库中获取数据
  • 将数据放入地图中
  • 以 JSON 格式发送回客户端

因此,在编写了大约 30 个 API 之后,我决定试一试,看看它在负载测试下的性能如何。

测试 1: ab -n 1 -c 1 http://localhost:8000/sales/report 结果是 "Time per request: 72.623 [ms] (mean)" 。

测试 2: ab -n 100 -c 100 http://localhost:8000/sales/report 结果是“每次请求的时间:4548.155 [ms](平均值)”(没有 MYSQL 错误)。

在第二次测试中,这个数字是如何突然从 72.623 飙升到 4548 毫秒的?我们预计每天会有数千个请求,所以我需要在最终发布之前解决这个问题。当我看到这些数字时,我很惊讶;我简直不敢相信。我知道 GO 可以做得更好。

关于服务器和设置的基本信息:

  • 使用 GO 1.5
  • 16GB 内存
  • GOMAXPROCS 正在使用所有 8 个内核
  • db.SetMaxIdleConns(1000)
  • db.SetMaxOpenConns(1000)(还确保我们使用的是 连接)
  • 通过 unix socket 连接到 MYSQL
  • 系统在 Ubuntu 下运行

我们正在使用的外部库:

  • github.com/go-sql-driver/mysql

  • github.com/gorilla/mux

  • github.com/elgs/gosqljson

任何想法可能导致这种情况? .我看了一下这个post,但没有像我上面提到的那样工作,我从来没有遇到任何 MYSQL 错误。提前感谢您提供的任何帮助。

【问题讨论】:

  • 我相信您明白,如果不进一步了解正在做的事情,我们可以说的很少。你描述它的方式听起来太简单了,不会那么慢。
  • database/sql 已经有连接池,所以如果你在上面写了某种池,那会伤害你。你真的应该只需要在你的进程的生命周期内调用一次 sql.Open 。如果您的 sql 不重要,您可能无法从 MySQL 中获得更多吞吐量。 MySQL 在并发复杂查询时并不总是具有良好的性能。
  • 当您说将其放入地图时,这可能意味着任何事情。你为什么不分析子组件并更好地解决它
  • 查询本身并不复杂。只是从结构良好的数据库表中选择查询。运行时间为 0.06(未缓存)。当然,当它被数据库缓存时,这个数字会下降。
  • 发布端点/处理程序的代码。常见问题包括 a) 为每个请求创建新的数据库池,而不是重用池成员; b) 将数千行复制到新的地图/切片中; c) 反映太多。话虽如此,4548ms 很多,可能是由网络级别的某些原因引起的(MySQL 是否配置为允许超过 100 个 conns?)

标签: mysql go


【解决方案1】:

您的帖子没有足够的信息来说明为什么您的程序没有按照您的预期执行,但我认为仅这个问题就值得回答:

在第二次测试中,这个数字是如何突然从 72.623 秒飙升到 4548 毫秒的?

在您的第一个测试中,您执行了一个请求 (-n 1)。在您的第二次测试中,您同时执行了 100 个运行中的请求 (-c 100 -n 100)。

您提到您的程序与外部数据库通信,您的程序必须等待该资源响应。当您同时发送 1,000 个请求时,您了解数据库的性能吗?你没有提到这一点。 Go 当然可以毫不费力地在一秒钟内处理数百个并发请求,但这取决于你在做什么以及你是如何做的。如果您的程序无法像进来一样快地完成请求,它们就会堆积起来,从而导致高延迟。

您告诉我们的这些测试对于了解您的服务器在“正常”情况下的表现都没有帮助 - 您说这将是“每天数千个请求”(这不是很具体,但我会接受意思是“几秒钟”)。然后看看-c 4 -n 1000 会更有趣,或者是在较长时间内运行服务器的东西,有许多并发请求,这更像你期望得到的。

【讨论】:

  • 感谢您的意见,先生。但是,我能够使用 Mock DB 响应查明问题。在不调用 DB 函数的情况下,我每秒收到 7000 个请求。但是,当我调用 QueryDbToMap github.com/elgs/gosqljson/blob/master/gosqljson.go 时,该数字下降到每秒 20 个请求。我尝试自行运行查询,它需要 0.06 才能运行(未缓存)。
  • 我用 Mock DB 响应运行了相同的测试,得到 5821 req/秒。如果您必须猜测每秒可以处理多少个请求;结构良好的数据库表会是什么?
【解决方案2】:

我不熟悉 gosqljson 包,但是您说您的“查询本身并不复杂”并且您正在针对“结构良好的数据库表”运行它,所以我建议删除 gosqljson 和绑定您的查询结果为结构,然后将这些结构编组为 json。与对所有内容使用 map[string]interface{} 相比,这应该更快且内存抖动更少。

但我不认为 gosqljson 可能那么慢,所以它可能不是罪魁祸首。

您进行第二次基准测试的方式没有帮助。

测试 2:ab -n 100 -c 100 http://localhost:8000/sales/report

这并不是在测试您处理并发请求的速度,而是在测试您与 MySQL 建立连接的速度。您只执行了 100 个查询并使用了 100 个请求,这意味着 Go 可能大部分时间都在与 MySQL 建立多达 100 个连接。考虑到它为满足每个查询所做的所有其他事情,Go 可能甚至没有时间重用任何数据库连接,然后,繁荣,测试结束了。您需要将最大连接数设置为 50 并运行 10,000 个查询,以查看在已建立数据库连接池后并发请求需要多长时间;现在你基本上是在测试 Go 建立一个数据库连接池需要多长时间。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多