【问题标题】:sqlalchemy and SQLite shared cachesqlalchemy 和 SQLite 共享缓存
【发布时间】:2015-03-10 17:47:16
【问题描述】:

当:memory: 数据库使用特殊 URI(根据sqlite.org)打开时,SQLite 支持“共享缓存”:

[T]同一个内存数据库可以被两个或多个数据库打开 连接如下:

rc = sqlite3_open("file::memory:?cache=shared",&db);

我可以在 Python 3.4 中通过使用 URI parameter 代替 sqlite3.connect() 来利用这一点:

sqlite3.connect('file::memory:?cache=shared', uri=True)

但是,我似乎无法为 SQLAlchemy 获得同样的效果:

engine = sqlalchemy.create_engine('sqlite:///:memory:?cache=shared')
engine.connect()
...
TypeError: 'cache' is an invalid keyword argument for this function

有没有办法让 SQLAlchemy 使用共享缓存?

编辑:
在 Python 3.4 上,我可以使用 create_engine 的 creator 参数来解决问题,但问题仍然存在于其他 Python 版本:

creator = lambda: sqlite3.connect('file::memory:?cache=shared', uri=True)
engine = sqlalchemy.create_engine('sqlite://', creator=creator)
engine.connect()

【问题讨论】:

  • 如果不将新的 sqlite3 模块功能反向移植到早期版本,我认为您无法做到这一点。

标签: python sqlite sqlalchemy


【解决方案1】:

您应该避免在较旧的 Python 版本上传递uri=True,问题将得到解决:

import sqlite3
import sys

import sqlalchemy


DB_URI = 'file::memory:?cache=shared'
PY2 = sys.version_info.major == 2
if PY2:
    params = {}
else:
    params = {'uri': True}

creator = lambda: sqlite3.connect(DB_URI, **params)

engine = sqlalchemy.create_engine('sqlite:///:memory:', creator=creator)
engine.connect()

【讨论】:

    【解决方案2】:

    关于 SQLite 方言的 SQLAlchemy 文档详细描述了问题和解决方案:

    线程/池行为

    Pysqlite 的默认行为是禁止 在多个线程中使用单个连接。这是 最初打算与旧版本的 SQLite 一起工作 支持各种情况下的多线程操作。在 特别是,较旧的 SQLite 版本不允许 :memory: 数据库 在任何情况下都可以在多个线程中使用。

    Pysqlite 确实包含一个现在未记录的标志,称为 check_same_thread 这将禁用此检查,但请注意 pysqlite 连接仍然不能安全地同时使用 多个线程。特别是,任何语句执行调用都会 需要外部互斥,因为 Pysqlite 不提供 除其他外,错误消息的线程安全传播。所以虽然 甚至:memory: 数据库也可以在现代 SQLite 中的线程之间共享, Pysqlite 没有提供足够的线程安全性来使这种用法值得 它。

    SQLAlchemy 设置池以使用 Pysqlite 的默认行为:

    • 当指定:memory: SQLite 数据库时,默认方言 将使用SingletonThreadPool。此池维护单个连接 每个线程,以便在当前线程内对引擎的所有访问 使用相同的 :memory: 数据库 - 其他线程将访问 不同的:memory: 数据库。

    • 指定基于文件的数据库时,方言将使用NullPool 作为连接的来源。此池关闭并丢弃 立即返回池的连接。 SQLite 基于文件的连接具有极低的开销,因此池化不是 必要的。该方案还可以防止再次使用连接 在不同的线程中,最适合使用 SQLite 的粗粒度文件 锁定。

    在多线程中使用内存数据库

    使用:memory: 数据库 在多线程场景中,必须共享同一个连接对象 在线程之间,因为数据库仅存在于该范围内 联系。 StaticPool 实现将维护一个单一的 全局连接,check_same_thread 标志可以传递给 Pysqlite 为False:

    from sqlalchemy.pool import StaticPool
    engine = create_engine('sqlite://',
                  connect_args={'check_same_thread':False},
                  poolclass=StaticPool)
    

    请注意,在多个线程中使用 :memory: 数据库需要最新版本的 SQLite。

    来源:https://docs.sqlalchemy.org/en/13/dialects/sqlite.html#threading-pooling-behavior

    【讨论】:

    • 亲爱的上帝,事实上每个线程(不是进程)都有自己的自己的隔离数据库(可以' t 与另一个线程的数据库交谈)是微妙的。这篇文章为我节省了几天的精力。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-04
    • 2023-03-07
    • 2012-12-11
    • 2012-03-20
    • 1970-01-01
    • 2011-07-27
    相关资源
    最近更新 更多