自动重新连接PostgreSQL数据库

12

我有以下代码:

import psycopg2
conn = psycopg2.connect(database="****", user="postgres", password="*****", host="localhost", port="5432")
print ("Opened database successfully")
cur = conn.cursor()
cur.execute('''select * from xyz''')
print ("Table created successfully")
conn.commit()
conn.close()

我有大约50-60个复杂查询,但问题是PostgreSQL数据库有时会抛出以下错误:

Traceback (most recent call last):
  File "C:/Users/ruw2/Desktop/SQL.py", line 87, in <module>
    cur.execute('''select * from xyz;''')
psycopg2.DatabaseError: server conn crashed?
server closed the connection unexpectedly. 
This probably means the server terminated abnormally before or while processing the request.

似乎连接丢失了,我该如何自动连接到Postgre数据库呢?感谢您的帮助。


很难猜测,最有可能是连接超时,在这种情况下,大多数数据库适配器都提供了一个标志来自动重新连接。另一个选择是服务器真的崩溃了,在这种情况下,请检查日志;还有一种可能是客户端和数据库服务器之间的互联网不稳定(如果localhost:5432被转发到其他地方)。还有一种可能性是死锁(不太可能),或者结果集太大,导致服务器或客户端崩溃(尝试使用更新版本)。 - Dima Tisnek
谢谢您的回复,但是当我手动运行它时,它可以正常运行,但是当我通过Python代码自动化运行时,会出现错误。是否有任何自动重新连接选项... - iahmed
你在“Python代码自动化”这个词上就把我给弄丢了。看起来你有更大的问题…… - Dima Tisnek
2个回答

13

我会依靠装饰器 - retry 和 reconnect:

import psycopg2
from tenacity import retry, wait_exponential, stop_after_attempt
from typing import Callable

def reconnect(f: Callable):
    def wrapper(storage: DbStorage, *args, **kwargs):
        if not storage.connected():
            storage.connect()

        try:
            return f(storage, *args, **kwargs)
        except psycopg2.Error:
            storage.close()
            raise

    return wrapper


class DbStorage:

    def __init__(self, conn: string):
        self._conn: string = conn
        self._connection = None

    def connected(self) -> bool:
        return self._connection and self._connection.closed == 0

    def connect(self):
        self.close()
        self._connection = psycopg2.connect(self._conn)

    def close(self):
        if self.connected():
            # noinspection PyBroadException
            try:
                self._connection.close()
            except Exception:
                pass

        self._connection = None

    @retry(stop=stop_after_attempt(3), wait=wait_exponential())
    @reconnect
    def get_data(self): # pass here required params to get data from DB
        # ..
        cur = self._connection.cursor()
        cur.execute('''select * from xyz''')
        # ..

10

捕获异常并重新连接:

while True:
    conn = psycopg2.connect(database="****", user="postgres", password="*****", host="localhost", port="5432")
    cur = conn.cursor()
    try:
        cur.execute('''select * from xyz''')
    except psycopg2.OperationalError:
        continue
    break;

如果我使用上面的代码,如果出现异常,那么它会回到try块并执行任务吗?基本上,我想在出现异常时重新连接并执行所需的查询。感谢您的回复。 - iahmed
2
它不会返回到try块,而是会返回到while循环的开头。 - Mike Lawson
如 @Mike 所评论的那样,它将重新启动“while循环”,并重新连接。 - Clodoaldo Neto
@ClodoaldoNeto@mike lawson @所有人,感谢你们的建议,它有效。 - iahmed
1
如果我错了,请纠正我。我认为 OperationalError 不是一个好的异常来捕获和重试。它的一些子类可以这样做,但其他一些子类仅仅通过重试就没有成功的机会。为了100%正确,您可能需要捕获 psycopg2.Error 并将其 pgcode 与您必须详尽准备的错误代码列表进行比较。 - digenishjkl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接