当使用连接池时,以及扩展到使用通过create_engine()创建的引擎时,至关重要的是 使用通过create_engine()创建的引擎时,至关重要的是,池中的 连接不能共享给分叉的进程。TCP连接是 表示为文件描述符,它通常跨越进程 边界,这意味着这将导致对文件描述符的并发访问。 代表两个或多个完全独立的 Python 解释器的状态。

按照我的理解,如果我创建连接池。

self.engine = create_engine('postgresql://{user}:{password}@{host}:{port}/{db}'.format(
    user=Configuration().get(section='repository', option='user'),
    password=Configuration().get(section='repository', option='password'),
    host=Configuration().get(section='repository', option='host'),
    port=Configuration().get(section='repository', option='port'),
    db=Configuration().get(section='repository', option='database')
), echo=False, pool_size=3)
self.session = sessionmaker(self.engine, expire_on_commit=False)

and then call self.session() in different threads i will have 3 different connections which are used in N different threads. Does it mean that only 3 concurrent thread will do some work while others will wait until one or more thread will call session.close()? Or there is a chance that >2 threads will use the same connection simultaneously?

NullPool是否更安全(因为每个新会话都是一个新的连接)?

self.engine = create_engine('postgresql://{user}:{password}@{host}:{port}/{db}'.format(
            user=Configuration().get(section='repository', option='user'),
            password=Configuration().get(section='repository', option='password'),
            host=Configuration().get(section='repository', option='host'),
            port=Configuration().get(section='repository', option='port'),
            db=Configuration().get(section='repository', option='database')
        ), echo=False, poolclass=NullPool)

一般的问题是:在这种情况下,使用同一个连接池可以吗?

engine = create_engine('connection_string', echo=False, pool_size=3)
Session = sessionmaker(engine)
def some_function():
    session = Session()
pool = Pool(processes=10)
pool.map(some_function)
pool.close()
pool.join()
    
3 个评论
@IljaEverilä, 但我还是不明白这部分:我到底能不能在多个线程中使用同一个连接池?
现在看来,我得到了主要的想法
python
multithreading
sqlalchemy
Nikita Ryanov
Nikita Ryanov
发布于 2018-08-09
2 个回答
Ilja Everilä
Ilja Everilä
发布于 2020-12-03
已采纳
0 人赞同

总而言之,在线程和进程之间似乎存在着一种混合。该问题一开始就问SQLAlchemy连接池是否是线程安全的,但最后以一个代码例子结束,该例子使用了multiprocessing.对 "一般问题 "的简短回答是:不,如果使用分叉,你不应该在进程边界上共享一个引擎和它相关的连接池。不过,也有例外。

池的实现本身是线程安全的,通过代理一个替换代码1】也是线程安全的。因为引擎除了保持对池的引用外,并不持有状态。另一方面,从池中检查出来的连接是not线程安全的,以及也不是一个Session.

文档说,连接池也不是为多线程设计的。

这有一点误读,因为文档中的原话是关于通过共享连接池的process boundaries, if forking is used. This will likely lead to trouble, because beneath the SQLAlchemy and DB-API layers there is usually a TCP/IP socket or a file handle,以及those should not be operated on concurrently.

在这种特殊情况下,使用NullPool将是安全的,而其他的则不安全,因为它根本没有池,所以连接不会在进程之间共享,除非有人不顾一切地这么做。

这是否意味着只有3个同时存在的航线 will do some work while others will wait until one or more 航线 will call session.close()?

假设一个QueuePool在使用中,设置的大小不是一个硬性限制,有一些溢出的空间。大小决定了在池中持久保持的连接数。如果达到了溢出限制,该调用将等待timeout秒后才放弃,并举起一个TimeoutError,如果没有连接可用。

Or there is a chance that >2 航线s将同时使用同一个连接?

Two or more 航线s将不能意外地从一个池子里签出相同的连接,除了一个StaticPool, but one could explicitly share it between 航线s after (don't).

In the end, "使用引擎和连接的工作--基本用法"涵盖了问题的主要部分。

单个Engine代表进程管理许多单独的DBAPI连接,并且目的是为了以并行方式调用[强调是后加的]。

对于一个使用os.fork系统调用的多进程应用程序,或者例如Python的multiprocessing模块,通常需要为每个子进程使用单独的Engine。这是因为Engine维护了对连接池的引用,最终引用了DBAPI连接--这些连接往往不能跨进程移植。被配置为不使用池的Engine(通过使用NullPool来实现)就没有这个要求。

惊人的答案!谢谢你 替换代码0】可能我还是混淆了这个术语
A process可以由一个或多个线程这两个项目是同时进行的,也可能是平行进行的。两者之间的界限有时会显得有些模糊不清.
hwjp
hwjp
发布于 2020-12-03
0 人赞同

如果这对其他人有帮助的话--而且这实际上是对另一个问题的回答,那就是。

SQLAlchemy是否对同一线程中的所有引擎使用同一个连接池?

答案是否定的。 正如@ilja-everila指出的,SQLAexpects你可以在每个进程中使用一个engine。 因此,如果你做

engine1 = create_engine(...)
engine2 = create_engine(...)
engine1.pool is engine2.pool   # <- False
# so although pool_size=5, you can open more than 5 total connections
# because each engine has separate pools
connections1 = [engine1.connect() for _ in range(5)]
connections2 = [engine1.connect() for _ in range(5)]