【问题标题】:How does pgBouncer help to speed up DjangopgBouncer 如何帮助加速 Django
【发布时间】:2012-05-12 06:38:19
【问题描述】:

我有一些基于 gevent 的管理命令。由于我的管理命令发出数千个请求,因此我可以使用 Gevent 将所有套接字调用转换为非阻塞调用。这确实加快了我的应用程序,因为我可以同时发出请求。

目前我的应用程序的瓶颈似乎是 Postgres。这似乎是因为用于连接 Django 的 Psycopg 库是用 C 编写的,不支持异步连接。

我还了解到使用 pgBouncer 可以将 Postgres 速度提高 2 倍。这听起来不错,但如果有人能解释一下 pgBouncer 的工作原理和帮助,那就太好了?

谢谢

【问题讨论】:

  • 您的数据库模型也有可能与您对其发起的查询不匹配。通常,与从磁盘获取数据块所需的工作相比,网络开销非常小,而且:这不会降低性能,只会降低延迟。 (可能连接/断开连接非常频繁的情况除外)

标签: python django postgresql connection-pooling pgbouncer


【解决方案1】:

除了节省连接和断开连接的开销之外,连接池可以将大量的客户端连接汇集到少量的实际数据库连接。在 PostgreSQL 中,活动数据库连接的最佳数量通常在 ((2 * core_count) + Effective_spindle_count) 左右。超过这个数字,吞吐量和延迟都会变得更糟。 注意:最近的版本提高了并发性,所以在 2022 年我会推荐类似 ((4 * core_count) + Effective_spindle_count) 的东西。

有时人们会说“我要支持 2000 个用户,并且响应时间快”。几乎可以保证,如果您尝试使用 2000 个实际数据库连接来执行此操作,性能将非常糟糕。如果您有一台具有四个四核处理器的机器并且活动数据集已完全缓存,那么通过将请求集中到大约 35 个数据库连接中,您将看到这 2000 个用户的性能要好得多。

要理解为什么这是真的,这个思想实验应该会有所帮助。考虑一个假设的数据库服务器机器,它只有一个资源可以共享——一个内核。该核心将在所有并发请求之间平等地进行时间切片,而不会产生开销。假设有 100 个请求同时进入,每个请求都需要一秒钟的 CPU 时间。核心对所有这些都起作用,在它们之间进行时间切片,直到它们都在 100 秒后完成。现在考虑如果您在前面放置一个连接池会发生什么情况,该连接池将接受 100 个客户端连接,但一次只向数据库服务器发出一个请求,将在连接繁忙时到达的所有请求放入队列中。现在当 100 个请求同时到达时,一个客户端在 1 秒内得到响应;另一个在 2 秒内得到响应,最后一个客户端在 100 秒内得到响应。没有人需要等待更长的时间才能得到响应,吞吐量是一样的,但平均延迟是 50.5 秒而不是 100 秒。

一个真正的数据库服务器有更多的资源可以并行使用,但同样的原则也成立,一旦它们饱和,你只会通过添加更多的并发数据库请求来伤害事情。它实际上比示例更糟糕,因为任务越多,任务切换越多,锁和缓存争用增加,L2 和 L3 缓存行争用,以及许多其他会影响吞吐量和延迟的问题。最重要的是,虽然较高的 work_mem 设置可以通过多种方式帮助查询,但该设置是每个连接的每个计划节点的限制,因此您需要大量连接保持这个非常小,以避免刷新缓存甚至导致交换,这会导致计划变慢或哈希表溢出到磁盘之类的事情。

一些数据库产品有效地在服务器中构建了一个连接池,但是 PostgreSQL 社区的立场是,由于最好的连接池是在更接近客户端软件的地方完成的,他们将把它留给用户来管理。大多数池化程序将有一些方法将数据库连接限制为硬数字,同时允许更多的并发客户端请求,并根据需要对它们进行排队。这就是您想要的,它应该在事务的基础上完成,而不是每个语句或连接。

【讨论】:

  • 优秀的答案。我完全同意。
  • 这些前端嬉皮士都希望尽可能快地建立和断开连接,如果无法达到其自然高状态,则将连接池放在前面。我喜欢 2*ncore + nspindle 公式。每个进程都被认为在磁盘读取中被阻塞。
  • @kgrittn 我假设在您上面的思想实验中,在没有其他请求的情况下,每个查询需要一秒钟才能运行?
  • @MichaelMior:是的,我绝对是想把它放在那里,但错过了。谢谢。编辑以包含该假设。
  • @MichaelMior:在 PostgreSQL 中,每个客户端连接都有一个操作系统 进程,它依赖于操作系统进行调度。各种进程通过共享内存段、操作系统信号(如果可用)和自引用 UDP 套接字进行通信。
【解决方案2】:

PgBouncer 通过充当维护连接池的代理来减少建立连接的延迟。如果您要打开许多与 Postgres 的短期连接,这可能有助于加快您的应用程序。如果你只有少量的连接,你不会看到太多的胜利。

【讨论】:

  • 如果我理解这一点是正确的 - Django 仍然会一次又一次地创建连接,但 pgBouncer 减少了创建此连接所需的时间。我听说 Django 会为每个请求创建一个新连接。人们所说的请求是指获取页面的网络请求(这意味着在视图周期中执行的每个命令都通过一个数据库连接)还是请求意味着每个单独的数据库命中(SELECT、INSERT、UPDATE 和 DELETE ) 在这种情况下,每个命令都将在新连接中执行,即使它们在同一个视图周期中
  • 是的,Django 会创建一个新的连接,但是这个连接会建立得更快,因为它会连接到一个本地的 PgBouncer 实例。 Django 将为每个 Web 请求使用新连接,而不是数据库查询。
  • 您可能会发现this question 有一些更有趣的信息。但请注意,每次请求都会打开新连接是有原因的。如果请求遇到错误,则事务可能未正确关闭(除其他外)导致意外结果。
  • 有没有一种方法可以检查在我的管理命令运行期间 Django 创建和销毁连接的次数。我可以检查一下,如果 DJango 建立了很多新的连接,使用 pgBouncer 会很好,否则我会研究另一种情况。它是一个管理命令而不是网页请求,所以我想知道数据库连接是否真的只创建一次。我的管理命令有数以千计的数据库命中。谢谢。
  • 您可以连接到信号django.db.backends.signals.connection_created,然后进行一些记录。 (请注意,您不希望在生产中这样做,因为它会增加不必要的开销。)
猜你喜欢
  • 1970-01-01
  • 2023-03-30
  • 2020-01-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-15
  • 1970-01-01
相关资源
最近更新 更多