【问题标题】:How to use sqlite across multiple (spawned) python processes via sqlalchemy如何通过 sqlalchemy 在多个(生成的)python 进程中使用 sqlite
【发布时间】:2021-05-14 15:23:30
【问题描述】:

我有一个名为 db.py 的文件,代码如下:

from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker


engine = create_engine('sqlite:///my_db.sqlite')
session = scoped_session(sessionmaker(bind=engine,autoflush=True))

我正在尝试在使用spawn 上下文开始的各种子进程中导入此文件(可能很重要,因为适用于fork 的各种修复似乎不适用于spawn

导入语句类似于:

from db import session

然后我随意使用这个会话而不用担心并发,假设SQLite的内部锁定机制会对事务进行排序以避免并发错误,我并不关心事务顺序。

这似乎会导致如下错误: sqlite3.ProgrammingError: SQLite objects created in a thread can only be used in that same thread. The object was created in thread id 139813508335360 and this is thread id 139818279995200.

请注意,这似乎并没有直接影响我的程序,每笔交易都顺利进行,但我仍然担心是什么原因造成的。

我的理解是scoped_session 是线程本地的,所以我可以随意导入它而不会出现问题。此外,我的假设是 sqlalchemy 将始终处理连接的关闭 sqllite 将处理排序(即让会话等待另一个会话结束,直到它可以执行任何事务)。

显然,这些假设之一是错误的,或者我误解了这里机制的一些基本内容,但我不太清楚是什么。任何建议都会很有用。

【问题讨论】:

  • 我不认为这会解决你的问题,但请注意,SQLite 并不总是能很好地处理多线程(尽管我的 python 发行版中的默认配置 在默认模式,即序列化)。见this

标签: python python-3.x sqlite sqlalchemy flask-sqlalchemy


【解决方案1】:

问题不在于线程本地会话,而在于原始连接对象与这些会话位于不同的线程中。 SQLite 默认禁用跨不同线程的连接。

您的问题最简单的答案是关闭 sqlite 的same thread checking。在 SQLAlchemy 中,您可以通过将其指定为数据库 URL 的一部分来实现此目的:

engine = create_engine('sqlite:///my_db.sqlite?check_same_thread=False')

我猜这至少会消除错误。

根据您在做什么,这可能仍然很危险 - 如果您确保您的交易是序列化的(即一个接一个,从不重叠或同时),那么您可能没问题。如果您不能保证,那么您就有数据损坏的风险,在这种情况下,您应该考虑 a) 使用可以处理并发写入的数据库后端,或 b) 创建一个仅管理 sqlite 读取和写入的中间应用程序或服务以及您的其他应用程序可以与之通信。后一种选择听起来很有趣,但请注意,当你最好只是旋转一个 Postgres 容器或其他东西时,你最终可能会重新发明轮子。

【讨论】:

  • 但是连接是线程本地的,那你怎么说它是跨线程使用的呢?这几乎没有意义,这就是为什么我有scoped_session。还是我正在创建的scoped_session 绑定到引擎最初创建的连接?这也不太可能,因为我并不总是注意到这些错误,只是在某些情况下异常中断线程。
  • 另外,我知道使用 PG 会更好,遗憾的是,这是一个相当便携的应用程序,必须通过 pypi 安装,所以我必须默认使用 sqlite :(
  • 我假设这样的“sincronization”功能应该已经由 sqlalchemy 或我可以插入的其他库实现。
猜你喜欢
  • 1970-01-01
  • 2017-08-21
  • 2020-10-15
  • 2014-04-05
  • 1970-01-01
  • 2016-03-19
  • 1970-01-01
  • 2018-09-03
  • 1970-01-01
相关资源
最近更新 更多