版本:1.1.0b2 |发布日期:2016年7月1日

SQLAlchemy 1.1文档

连接/引擎

如何配置日志?

请参阅Configuring Logging

我如何汇集数据库连接?我的连接是否合并?

在大多数情况下,SQLAlchemy会自动执行应用程序级连接池。除SQLite之外,Engine对象将QueuePool作为连接的来源。

有关更多详细信息,请参阅Engine ConfigurationConnection Pooling

如何将自定义连接参数传递给我的数据库API?

create_engine()调用直接通过connect_args关键字参数接受附加参数:

e = create_engine("mysql://scott:tiger@localhost/test",
                    connect_args={"encoding": "utf8"})

或者对于基本的字符串和整数参数,通常可以在URL的查询字符串中指定它们:

e = create_engine("mysql://scott:tiger@localhost/test?encoding=utf8")

“MySQL服务器已经消失”

这个错误有两个主要原因:

1. MySQL客户端关闭空闲一段时间的连接,默认为八个小时。这可以通过在Connection Timeouts中描述的create_engine()使用pool_recycle设置来避免。

2. MySQLdb DBAPI或类似的DBAPI的使用,以非线程安全的方式,或以其他不正确的方式使用。MySQLdb连接对象不是线程安全的 - 这扩展到任何链接到包含ORM Session的单个连接的SQLAlchemy系统。有关如何在多线程环境中使用Session的背景,请参见Is the session thread-safe?

为什么SQLAlchemy发出这么多的ROLLBACK?

SQLAlchemy目前假设DBAPI连接处于“非自动提交”模式 - 这是Python数据库API的默认行为,这意味着必须假定事务总是在进行中。连接池在连接返回时发出connection.rollback()这样就可以释放连接上剩余的事务资源。在Postgresql或MSSQL这样的表资源被大量锁定的数据库中,这是非常重要的,这样行和表就不会在不再使用的连接中锁定。否则应用程序可能会挂起。然而,这不仅仅是锁,而且对任何一种具有任何事务隔离的数据库(包括带有InnoDB的MySQL)都是同等重要的。任何仍旧在旧事务中的连接都将返回陈旧的数据,如果该数据已经在隔离内的连接上被查询的话。有关为什么您可能会在MySQL上看到过时数据的背景,请参阅http://dev.mysql.com/doc/refman/5.1/en/innodb-transaction-model.html

我在MyISAM上 - 如何关闭它?

连接池的连接返回行为的行为可以使用reset_on_return来配置:

from sqlalchemy import create_engine
from sqlalchemy.pool import QueuePool

engine = create_engine('mysql://scott:tiger@localhost/myisam_database', pool=QueuePool(reset_on_return=False))

我在SQL Server上 - 如何将这些ROLLBACKs转换为COMMIT?

除了TrueFalse之外,reset_on_return还接受commitrollback None设置为commit会导致COMMIT,因为任何连接都返回到池:

engine = create_engine('mssql://scott:tiger@mydsn', pool=QueuePool(reset_on_return='commit'))

I am using multiple connections with a SQLite database (typically to test transaction operation), and my test program is not working!

如果使用SQLite :memory:数据库或0.7版之前的SQLAlchemy版本,则默认连接池是SingletonThreadPool,每个线程只维护一个SQLite连接。所以在同一个线程中使用的两个连接实际上是相同的SQLite连接。确保没有使用:memory:数据库,并使用NullPool,这是当前SQLAlchemy版本中非内存数据库的缺省值。

也可以看看

Threading/Pooling Behavior - info on PySQLite’s behavior.

在使用引擎时,如何获得原始DBAPI连接?

With a regular SA engine-level Connection, you can get at a pool-proxied version of the DBAPI connection via the Connection.connection attribute on Connection, and for the really-real DBAPI connection you can call the ConnectionFairy.connection attribute on that - but there should never be any need to access the non-pool-proxied DBAPI connection, as all methods are proxied through:

engine = create_engine(...)
conn = engine.connect()
conn.connection.<do DBAPI things>
cursor = conn.connection.cursor(<DBAPI specific arguments..>)

您必须确保将连接上的任何隔离级别设置或其他特定于操作的设置恢复到正常状态,然后才能将其返回到池中。

As an alternative to reverting settings, you can call the Connection.detach() method on either Connection or the proxied connection, which will de-associate the connection from the pool such that it will be closed and discarded when Connection.close() is called:

conn = engine.connect()
conn.detach()  # detaches the DBAPI connection from the connection pool
conn.connection.<go nuts>
conn.close()  # connection is closed for real, the pool replaces it with a new connection

如何在Python多处理或os.fork()中使用引擎/连接/会话?

多个python进程的关键目标是防止跨进程共享任何数据库连接。根据驱动程序和操作系统的具体情况,这里出现的问题从非工作连接到多个进程并发使用的套接字连接,导致断开的消息传递(后者通常是最常见的)。

SQLAlchemy Engine对象是指现有数据库连接的连接池。所以当这个对象被复制到子进程时,目标是确保没有数据库连接被继续。有三种一般的方法:

  1. 使用NullPool禁用池。这是最简单的一次性系统,它可以防止Engine多次使用任何连接。

  2. 在任何给定的Engine上调用Engine.dispose()在Python多处理中,像multiprocessing.Pool这样的结构包含了可以执行的“初始化器”钩子;否则在os.fork()的顶部或者Process对象开始子fork的位置,调用Engine.dispose()

  3. 事件处理程序可以应用于连接池,用于测试跨进程边界共享的连接,并使其无效。这看起来像下面这样:

    import os
    import warnings
    
    from sqlalchemy import event
    from sqlalchemy import exc
    
    def add_engine_pidguard(engine):
        """Add multiprocessing guards.
    
        Forces a connection to be reconnected if it is detected
        as having been shared to a sub-process.
    
        """
    
        @event.listens_for(engine, "connect")
        def connect(dbapi_connection, connection_record):
            connection_record.info['pid'] = os.getpid()
    
        @event.listens_for(engine, "checkout")
        def checkout(dbapi_connection, connection_record, connection_proxy):
            pid = os.getpid()
            if connection_record.info['pid'] != pid:
                # substitute log.debug() or similar here as desired
                warnings.warn(
                    "Parent process %(orig)s forked (%(newproc)s) with an open "
                    "database connection, "
                    "which is being discarded and recreated." %
                    {"newproc": pid, "orig": connection_record.info['pid']})
                connection_record.connection = connection_proxy.connection = None
                raise exc.DisconnectionError(
                    "Connection record belongs to pid %s, "
                    "attempting to check out in pid %s" %
                    (connection_record.info['pid'], pid)
                )

    这些事件在创建时立即应用于Engine

    engine = create_engine("...")
    
    add_engine_pidguard(engine)

上述策略将适应在进程之间共享Engine的情况。However, for the case of a transaction-active Session or Connection being shared, there’s no automatic fix for this; an application needs to ensure a new child process only initiate new Connection objects and transactions, as well as ORM Session objects. 对于Session对象,从技术上讲,只有在会话当前是事务绑定的情况下才需要这样做,然而单个Session的范围无论如何都是要保留在在任何情况下单个调用堆栈(例如,不是全局对象,不在进程或线程之间共享)。