SQLAlchemy-处理与数据库和会话的连接

我使用SQLAlchemy(非常好的ORM,但文档不够清晰)与PostgreSQL通讯
一切都很好,直到出现一种情况,即达到最大连接限制的postgres“崩溃”原因:不再允许连接(max_client_conn)
这种情况使我认为我做错了。经过几次实验后,我弄清楚了如何不再面对该问题,但是还剩下一些问题。

下面,您将看到没有和有提到问题的代码示例(在Python 3+中,PostgreSQL设置为默认设置),以及我想听听的内容最终是下列问题的答案:

  1. 上下文管理器到底对连接和会话做了什么?关闭会话并配置连接还是什么?

  2. 为什么在“ connect”方法中没有NullPool作为poolclass的情况下,第一个有效的代码示例作为问题的示例?

  3. 为什么在第一个示例中,对于所有查询,我只有一个到db的连接,但是在第二个示例中,对于每个查询,我都有单独的连接?(如果我理解不对,请使用“ pgbouncer”进行检查以纠正我)

  4. 当您对多个侦听脚本的脚本实例(或脚本中的单独线程)使用SQLAlchemy和PostgreSQL DB时,打开和关闭连接(和/或与Session一起使用)的最佳实践是什么,这是最佳做法,这些实例侦听请求并必须分别与每个会话进行会话?(我的意思是原始SQLAlchemy而不是Flask-SQLAlchemy或类似的东西)

    没有问题的代码的工作示例:

与数据库建立连接

from sqlalchemy.pool import NullPool  # does not work without NullPool, why?


def connect(user, password, db, host='localhost', port=5432):

    """Returns a connection and a metadata object"""

    url = 'postgresql://{}:{}@{}:{}/{}'.format(user, password, host, port, db)


    temp_con = sqlalchemy.create_engine(url, client_encoding='utf8', poolclass=NullPool)

    temp_meta = sqlalchemy.MetaData(bind=temp_con, reflect=True)


    return temp_con, temp_meta

使会话与DB一起工作的功能:


from contextlib import contextmanager


@contextmanager

def session_scope():

    con_loc, meta_loc = connect(db_user, db_pass, db_instance, 'localhost')

    Session = sessionmaker(bind=con_loc)


    """Provide a transactional scope around a series of operations."""

    session = Session()

    try:

        yield session

        session.commit()

    except:

        session.rollback()

        raise

查询示例:


with session_scope() as session:

    entity = session.query(SomeEntity).first()


qq_笑_17
浏览 317回答 3
3回答

月关宝盒

首先,您不应在connect()函数中重复创建引擎。在通常的做法是有一个单一的全球Engine每个数据库URL例如在应用程序中。Session由所创建的类也是如此sessionmaker()。上下文管理器到底对连接和会话做了什么?关闭会话并配置连接还是什么?您已经对其进行编程了,如果还不清楚,请阅读有关上下文管理器的一般信息。在这种情况下,如果在with语句控制的块内引发了异常,它将提交或回滚会话。这两个操作都将会话使用的连接返回到池(在您的情况下为)NullPool,因此仅关闭了连接。为什么在“ connect”方法中没有NullPool作为poolclass的情况下,第一个有效的代码示例作为问题的示例?和from sqlalchemy.pool import NullPool  # does not work without NullPool, why?如果没有NullPool引擎,您会反复创建池连接,因此,如果由于某种原因它们没有超出范围,或者它们的引用计数没有被清零,那么即使会话返回它们,它们也将保留连接。在第二个示例中,尚不清楚会话是否及时超出范围,因此它们也可能会保持连接。为什么在第一个示例中,对于所有查询,我只有一个到db的连接,但是在第二个示例中,对于每个查询,我都有单独的连接?(如果我理解不对,请更正我,正在使用“ pgbouncer”进行检查)第一个示例由于使用可以正确处理事务的上下文管理器和结束了连接的关闭NullPool,因此该连接返回到保镖器,该保镖器是另一个池层。第二个示例可能永远不会关闭连接,因为它缺少事务处理,但是由于给出的示例,目前尚不清楚。它还可能会保留您创建的单独引擎中的连接。问题集的第4点已在“会话基础”中的正式文档中进行了介绍,尤其是“何时构造会话,何时提交会话以及何时关闭会话?”。和“会话线程安全吗?” 。有一个例外:脚本的多个实例。您不应该在进程之间共享引擎,因此,为了在它们之间共享连接,您需要一个外部池,例如PgBouncer。

白板的微信

上下文管理器到底对连接和会话做了什么?关闭会话并配置连接还是什么?Python中的上下文管理器用于创建与该with语句一起使用的运行时上下文。简单来说,当您运行代码时:with session_scope() as session:    entity = session.query(SomeEntity).first()session是产生的会话。因此,对于您的上下文管理器如何处理连接和会话的问题,您要做的就是查看之后发生了yield什么,看看会发生什么。在这种情况下,它只是:try:    yield session    session.commit()except:    session.rollback()    raise如果您没有触发任何异常,它将为session.commit(),根据SQLAlchemy文档,它将“刷新未决的更改并提交当前事务。”为什么在“ connect”方法中没有NullPool作为poolclass的情况下,第一个有效的代码示例作为问题的示例?该poolclass参数只是告诉SQLAlchemy使用哪个子类Pool。但是,如果您通过NullPool此处,则是在告诉SQLAlchemy不要使用池。传入时,您实际上是在禁用池连接NullPool。从文档中:“要禁用池,请改为将poolclass设置为NullPool。” 我不能肯定地说,但使用NullPool可能会助您max_connection一臂之力。为什么在第一个示例中,对于所有查询,我只有一个到db的连接,但是在第二个示例中,对于每个查询,我都有单独的连接?(如果我理解不对,请更正我,正在使用“ pgbouncer”进行检查)我不太确定。我认为这与在第一个示例中如何使用上下文管理器有关,因此with块中的所有内容都将使用session生成器。在第二个示例中,您创建了一个函数,该函数将初始化新的函数Session并返回它,因此您无需找回生成器。我还认为这与您的NullPool使用有关,它可以防止连接池。对于NullPool每个查询,执行都是自己获取一个连接。当您对多个侦听脚本的脚本实例(或脚本中的单独线程)使用SQLAlchemy和PostgreSQL DB时,打开和关闭连接(和/或与Session一起使用)的最佳实践是什么,这是最佳做法,这些实例侦听请求并必须分别与每个会话进行会话?(我的意思是原始SQLAlchemy而不是Flask-SQLAlchemy或类似的东西)请参阅会话线程安全吗?为此,但是您需要对并发采取“不共享”方法。因此,在您的情况下,您需要脚本的每个实例在彼此之间不共享任何内容。您可能想查看使用引擎和连接。如果并发是您正在从事的工作,那么我不希望将会议弄得一团糟。那里有关于NullPool和并发的更多信息:对于使用os.fork系统调用的多进程应用程序,例如Python多处理模块,通常需要为每个子进程使用单独的Engine。这是因为引擎维护了对连接池的引用,该连接池最终引用了DBAPI连接-这些连接往往无法跨进程边界移植。配置为不使用池化的引擎(通过使用NullPool来实现)不具有此要求。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Python