代码之家  ›  专栏  ›  技术社区  ›  Nikita Ryanov

sqlalchemy中的连接池是线程安全的吗?

  •  2
  • Nikita Ryanov  · 技术社区  · 7 年前

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

    在使用连接池时,以及通过扩展 使用通过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()
    
    1 回复  |  直到 7 年前
        1
  •  9
  •   Ilja Everilä    7 年前

    总而言之,线程和进程之间似乎是混合的。问题的开始是询问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" 包括问题的主要部分:

    单曲 发动机 代表进程管理许多单独的DBAPI连接,并 旨在以并行方式调用 [重点补充]。

    ...

    对于使用 os.fork 系统调用,或者例如Python 多处理 模块,通常需要 发动机 用于每个子进程。这是因为 发动机 维护对最终引用DBAPI连接的连接池的引用-这些连接往往无法跨进程边界进行移植。一个 发动机 配置为不使用池(通过使用 空池 )没有此要求。