如何自动关闭 PostgreSQL 中的空闲连接?

一些客户端连接到 postgreql 数据库,但是保持连接打开。 有没有可能告诉 Postgresql 在一定程度的不作为之后关闭这些连接?

DR

如果您正在使用 Postgreql version > = 9.2
使用 我想到的解决办法 < br >

如果您不想编写任何代码
然后使用 < a href = “ https://stackoverflow. com/a/12403753/363573”> arqnid 的解决方案

如果您不想编写任何代码
而且您正在使用 Postgreql version > = 14
然后使用 Laurenz Albe 的解决方案

168193 次浏览

Connect through a proxy like PgBouncer which will close connections after server_idle_timeout seconds.

For those who are interested, here is the solution I came up with, inspired from Craig Ringer's comment:

(...) use a cron job to look at when the connection was last active (see pg_stat_activity) and use pg_terminate_backend to kill old ones.(...)

The chosen solution comes down like this:

  • First, we upgrade to Postgresql 9.2.
  • Then, we schedule a thread to run every second.
  • When the thread runs, it looks for any old inactive connections.
    • A connection is considered inactive if its state is either idle, idle in transaction, idle in transaction (aborted) or disabled.
    • A connection is considered old if its state stayed the same during more than 5 minutes.
  • There are additional threads that do the same as above. However, those threads connect to the database with different user.
  • We leave at least one connection open for any application connected to our database. (rank() function)

This is the SQL query run by the thread:

WITH inactive_connections AS (
SELECT
pid,
rank() over (partition by client_addr order by backend_start ASC) as rank
FROM
pg_stat_activity
WHERE
-- Exclude the thread owned connection (ie no auto-kill)
pid <> pg_backend_pid( )
AND
-- Exclude known applications connections
application_name !~ '(?:psql)|(?:pgAdmin.+)'
AND
-- Include connections to the same database the thread is connected to
datname = current_database()
AND
-- Include connections using the same thread username connection
usename = current_user
AND
-- Include inactive connections only
state in ('idle', 'idle in transaction', 'idle in transaction (aborted)', 'disabled')
AND
-- Include old connections (found with the state_change field)
current_timestamp - state_change > interval '5 minutes'
)
SELECT
pg_terminate_backend(pid)
FROM
inactive_connections
WHERE
rank > 1 -- Leave one connection for each application connected to the database

If you are using PostgreSQL >= 9.6 there is an even easier solution. Let's suppose you want to delete all idle connections every 5 minutes, just run the following:

alter system set idle_in_transaction_session_timeout='5min';

In case you don't have access as superuser (example on Azure cloud), try:

SET SESSION idle_in_transaction_session_timeout = '5min';

But this latter will work only for the current session, that most likely is not what you want.

To disable the feature,

alter system set idle_in_transaction_session_timeout=0;

or

SET SESSION idle_in_transaction_session_timeout = 0;

(by the way, 0 is the default value).

If you use alter system, you must reload configuration to start the change and the change is persistent, you won't have to re-run the query anymore if, for example, you will restart the server.

To check the feature status:

show idle_in_transaction_session_timeout;

If you use AWS with PostgreSQL >= 9.6, you have to do the following:

Create custom parameter group

go to RDS > Parameter groups > Create parameter group Select the version of PSQL that you use, name it 'customParameters' or whatever and add description 'handle idle connections'.

Change the idle_in_transaction_session_timeout value

Fortunately it will create a copy of the default AWS group so you only have to tweak the things that you deem not suitable for your use-case.

Now click on the newly created parameter group and search 'idle'.
The default value for 'idle_in_transaction_session_timeout' is set to 24 hours (86400000 milliseconds). Divide this number by 24 to have hours (3600000) and then you have to again divide 3600000 by 4, 6 or 12 depending on whether you want the timeout to be respectively 15, 10 or 5 minutes (or equivalently multiply the number of minutes x 60000, so value 300 000 for 5 minutes).

Assign the group

Last, but not least, change the group:

go to RDS, select your DB and click on 'Modify'.

Now under 'Database options' you will find 'DB parameter group', change it to the newly created group.

You can then decide if you want to apply the modifications immediately (beware of downtime).

I have the problem of denied connections as there are too much clients connected on Postgresql 12 server (but not on similar projects using earlier 9.6 and 10 versions) and Ubuntu 18.

I wonder if those settings

tcp_keepalives_idle
tcp_keepalives_interval

could be more relevant than

idle_in_transaction_session_timeout

idle_in_transaction_session_timeout indeed closes only the idle connections from failed transactions, not the inactive connections whose statements terminate correctly... the documentation reads that these socket-level settings have no impact with Unix-domain sockets but it could work on Ubuntu.

From PostgreSQL v14 on, you can set the idle_session_timeout parameter to automatically disconnect client sessions that are idle.

Up to PostgreSQL 13, you can use my extension pg_timeout.