文档说连接池也不是为多线程设计的:
至关重要的是,使用连接池时以及扩展时 使用通过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)
,然后在不同的线程中调用self.session()
,我将有3个不同的连接,它们在N个不同的线程中使用。
这是否意味着只有3个并发线程会执行某些工作,而其他线程会等到一个或多个线程将调用session.close()
时执行?还是有> 2个线程同时使用同一连接的可能性?
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()
答案 0 :(得分:5)
总而言之,线程和进程之间似乎混合在一起。该问题首先询问SQLAlchemy连接池是否是线程安全的,但以使用multiprocessing
的代码示例结尾。对于“一般问题”的简短回答是:不,如果使用分叉,则不应在进程边界上共享引擎及其关联的连接池。不过有例外。
池实现本身是线程安全的,并且通过代理Engine
is thread-safe as well都是线程安全的,因为引擎除了保留对池的引用之外不保存状态。另一方面,从池中检出的连接为not thread-safe和neither is a Session
。
文档说连接池也不是为多线程设计的:
有些误读,因为文档中的原始报价是关于在进程边界上共享连接池(如果使用了分叉的话)。这可能会导致麻烦,因为在SQLAlchemy和DB-API层下面通常有一个TCP / IP套接字或文件句柄,并且不应同时对其进行操作。
在这种特殊情况下,使用NullPool
是安全的,而其他情况则不安全,因为它根本不合并,因此进程之间将不会共享连接,除非一个人竭尽所能这样。
这是否意味着只有3个并发的线程会做一些工作,而其他线程会等到一个或多个线程将调用
session.close()
?
假设正在使用QueuePool
,则设置的大小不是硬限制,并且有一定的溢出空间。大小确定了要在池中持久保留的连接数。如果达到了溢出限制,则在没有可用连接的情况下,呼叫将等待timeout
秒,然后放弃并提出TimeoutError
。
还是> 2个线程会同时使用相同的连接?
两个或多个线程将无法意外地从池中检出相同的连接,除了StaticPool
之外,但一个人可以在(不)之后在线程之间显式共享它
最后,"Working with Engines and Connections - Basic Usage"涵盖了问题的主要部分:
单个
Engine
代表该流程管理许多单独的DBAPI连接,并且旨在以并发方式调用。...
对于使用
os.fork
系统调用的多进程应用程序,例如Pythonmultiprocessing
模块,通常需要为每个子进程使用单独的Engine
。这是因为Engine
维护了对连接池的引用,该连接池最终引用了DBAPI连接-这些连接往往无法跨进程边界移植。配置为不使用池化的Engine
(通过使用NullPool
实现)没有此要求。
答案 1 :(得分:0)
万一这对其他人有帮助的话-这实际上是另一个问题的答案,那就是:
SQLAlchemy是否对同一线程中的所有引擎使用相同的连接池?
答案是否定的。正如@ ilja-everila指出的那样,SQLA expects会在每个进程中使用单个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)]
因此,如果您来这里想知道为什么要最大化max_connections
,并且您的代码正在使用许多单独的engine
实例,即使它们在同一线程中,也就无法期望他们共享一个连接池。
连接池可能是线程安全的,但是它们对于每个引擎实例都是唯一的。
因此,您应该致力于为您的应用提供一个全局/单引擎实例。
从失败中学习!