4

试图了解以下问题的原因,我真的很头疼。我们正在使用以下库的组合:

SQLAlchemy最初使用,现在NullPool配置为使用QueuePool. 我还使用以下成语为每个线程启动一个新的数据库会话(根据我的理解)

Session = sessionmaker(bind=create_engine(classes.db_url, poolclass=QueuePool))

@contextmanager
def session_scope():
   session = Session()
   try:
      yield session
      session.commit()
   except:
      session.rollback()
      raise
   finally:
      session.close()

@bot.message_handler(content_types=['document'])
def method_handler:
   with session_scope() as session:
      do_database_stuff_here(session)

尽管如此,我仍然遇到这个烦人的异常:(sqlite3.ProgrammingError) SQLite objects created in a thread can only be used in that same thread

有任何想法吗?;) 特别是,我不明白另一个胎面如何可能在数据库操作之间的某个地方......这可能是讨厌的异常的原因

更新 1:如果我将 更改poolclassSingletonThreadPool,那么似乎不会再出现错误。然而,文档SQLAlchemy告诉它不是生产盛行。

4

2 回答 2

5

正如您在源代码pysqlite_check_thread中看到的,如果在任何线程中重用连接对象,sqlite 将在内部引发此异常。

通过使用 a QueuePool,您告诉 SQLAchemy 在多个线程之间重用连接是安全的。因此,它只会从池中为任何会话选择一个连接,无论它在哪个线程上。这就是您遇到错误的原因。第一次创建和使用连接时,你会没事的;但是下一次使用可能会在不同的线程上,因此检查失败。

这就是 SQLAlchemy要求使用其他池(例如SingletonThreadPool和)的原因NullPool

假设您使用的是基于文件的数据库,您应该使用NullPool. 这将为您提供良好的读取并发性。对于 sqlite 来说,写访问并发总是一个问题;如果你需要这个,你可能需要一个不同的数据库。

于 2016-10-24T09:23:22.077 回答
4

可能值得尝试的东西:使用scoped_session而不是你的contextmanager; scoped_session当从不同的线程访问它时,隐式地创建一个线程本地会话。一定也要使用NullPool.

from sqlalchemy.orm import scoped_session
sessionmaker(bind=create_engine(classes.db_url, poolclass=NullPool))
session = scoped_session()

请注意,您可以session直接使用 this 作用域,就好像它只是一个常规的一样session,即使它在使用时实际上是在幕后创建线程本地会话。

For scoped_session, 应该session.remove()在你完成后调用(即,在每个 之后method_handler)并根据需要显式调用session.commit()

理论上,您的上下文管理器应该为每个线程提供自己的会话,但是,由于缺乏更好的解释,我想知道上下文中是否有多个线程访问该会话。

于 2016-10-27T04:28:39.150 回答