我将如何配置 PGbouncer 以跨查询使用 SET search_path?
How would I configure PGbouncer to work with SET search_path across queries?
我有一个相当大的 PostgreSQL 数据库,其中有多达 300 个来自网络应用程序的连接客户端和一些后台处理。
我正在考虑添加 PGbouncer,因为据我所知,由于连接开销,postgresql 将无法继续很好地扩展这么多连接。
背景
我的网络应用程序是一个多租户应用程序。例如。我的网站在 www.my-app.de 有德语版,在 www.my-app.com 有美国版。每个国家/地区的数据在 postgresql 中拆分为单独的 SCHEMAS
。因此,对于每个 HTTP 请求,Web 应用程序通过设置 search_path
来启动请求,从而在 PostgreSQL 中的数据集之间切换:if www.my-app.com then SET search_path = 'us'
。从应用程序的角度来看,这确实很方便,但是,我认为这与进行连接池的可能性相冲突?
问题
因此在每个 HTTP 请求中,为整个请求设置 search_path
,并且在请求中可能有 许多 对数据库的查询。据我了解,如果我让 PGbouncer 进行连接池,我可能会冒险在请求中第一个查询转到 search_path 设置为 us
的连接,而下一个查询到具有search_path 设置为 de
。
问题
PGbouncer 有什么办法可以避免这种行为吗?或者我可以使用一种替代模式来减少连接开销?
PostgreSQL Wiki 上有一个很好的 PgBouncer 限制列表。 SET
在该列表中。
假设您希望在 PgBouncer 中使用 pool_mode = transaction
- 这是最明智的模式,并且您有一些依赖于会话设置的应用程序代码(如 search_path
),保留会话状态的唯一方法是使用事务。
如果您的应用只是创建到 PgBouncer 的连接,在其上运行 SET search_path TO us
,然后运行多个 SELECT - 它不会工作。
这很容易证明 - 只需将 psql 连接到 PgBouncer 并使用 SET 命令:
(postgres.example.com:6432) prod=# SET client_min_messages TO debug;
SET
(postgres.example.com:6432) prod=# SHOW client_min_messages ;
client_min_messages
---------------------
notice
(1 row)
如我们所见,会话状态不会持续存在。您需要交易:
(postgres.example.com:6432) prod=# begin;
BEGIN
(postgres.example.com:6432) prod=# SET client_min_messages TO debug;
SET
(postgres.example.com:6432) prod=# SHOW client_min_messages ;
client_min_messages
---------------------
debug
(1 row)
(postgres.example.com:6432) prod=# COMMIT;
COMMIT
换句话说 - 要启用事务池,事务必须 无状态 - 与 HTTP 请求的方式完全相同,其中包含所有状态信息(以 cookie 的形式,会话 ID 等)。
显然,在您的情况下,这意味着修改应用程序代码。在每个调用周围添加一个 "wrapper"。这有点丑陋,如果您必须走那条路,请考虑重写应用程序以明确使用国家/地区代码,作为参数或数据库列。
我有一个相当大的 PostgreSQL 数据库,其中有多达 300 个来自网络应用程序的连接客户端和一些后台处理。
我正在考虑添加 PGbouncer,因为据我所知,由于连接开销,postgresql 将无法继续很好地扩展这么多连接。
背景
我的网络应用程序是一个多租户应用程序。例如。我的网站在 www.my-app.de 有德语版,在 www.my-app.com 有美国版。每个国家/地区的数据在 postgresql 中拆分为单独的 SCHEMAS
。因此,对于每个 HTTP 请求,Web 应用程序通过设置 search_path
来启动请求,从而在 PostgreSQL 中的数据集之间切换:if www.my-app.com then SET search_path = 'us'
。从应用程序的角度来看,这确实很方便,但是,我认为这与进行连接池的可能性相冲突?
问题
因此在每个 HTTP 请求中,为整个请求设置 search_path
,并且在请求中可能有 许多 对数据库的查询。据我了解,如果我让 PGbouncer 进行连接池,我可能会冒险在请求中第一个查询转到 search_path 设置为 us
的连接,而下一个查询到具有search_path 设置为 de
。
问题
PGbouncer 有什么办法可以避免这种行为吗?或者我可以使用一种替代模式来减少连接开销?
PostgreSQL Wiki 上有一个很好的 PgBouncer 限制列表。 SET
在该列表中。
假设您希望在 PgBouncer 中使用 pool_mode = transaction
- 这是最明智的模式,并且您有一些依赖于会话设置的应用程序代码(如 search_path
),保留会话状态的唯一方法是使用事务。
如果您的应用只是创建到 PgBouncer 的连接,在其上运行 SET search_path TO us
,然后运行多个 SELECT - 它不会工作。
这很容易证明 - 只需将 psql 连接到 PgBouncer 并使用 SET 命令:
(postgres.example.com:6432) prod=# SET client_min_messages TO debug;
SET
(postgres.example.com:6432) prod=# SHOW client_min_messages ;
client_min_messages
---------------------
notice
(1 row)
如我们所见,会话状态不会持续存在。您需要交易:
(postgres.example.com:6432) prod=# begin;
BEGIN
(postgres.example.com:6432) prod=# SET client_min_messages TO debug;
SET
(postgres.example.com:6432) prod=# SHOW client_min_messages ;
client_min_messages
---------------------
debug
(1 row)
(postgres.example.com:6432) prod=# COMMIT;
COMMIT
换句话说 - 要启用事务池,事务必须 无状态 - 与 HTTP 请求的方式完全相同,其中包含所有状态信息(以 cookie 的形式,会话 ID 等)。
显然,在您的情况下,这意味着修改应用程序代码。在每个调用周围添加一个 "wrapper"。这有点丑陋,如果您必须走那条路,请考虑重写应用程序以明确使用国家/地区代码,作为参数或数据库列。