我有一个现有的 SQLite 3 数据库文件,我需要对其进行一些广泛的计算。从文件中进行计算非常慢,而且由于文件不大(大约 10 MB),因此将其加载到内存中应该不会有任何问题。
有没有Pythonic的方法将现有文件加载到内存中以加快计算速度?
这是我为我的烧瓶应用程序编写的片段:
import sqlite3
from io import StringIO
def init_sqlite_db(app):
# Read database to tempfile
con = sqlite3.connect(app.config['SQLITE_DATABASE'])
tempfile = StringIO()
for line in con.iterdump():
tempfile.write('%s\n' % line)
con.close()
tempfile.seek(0)
# Create a database in memory and import from tempfile
app.sqlite = sqlite3.connect(":memory:")
app.sqlite.cursor().executescript(tempfile.read())
app.sqlite.commit()
app.sqlite.row_factory = sqlite3.Row
sqlite3.Connection.backup(...)
呢? “即使其他客户端正在访问 SQLite 数据库,或者同时通过同一连接访问该数据库,此方法也会对 SQLite 数据库进行备份。”可用性:SQLite 3.6.11 或更高版本。 3.7版本新功能。
import sqlite3
source = sqlite3.connect('existing_db.db')
dest = sqlite3.connect(':memory:')
source.backup(dest)
sqlite3.Connection.iterdump
“[r]返回一个迭代器,以 SQL 文本格式转储数据库。在保存内存数据库以供以后恢复时很有用。此函数提供与 sqlite3 中的 .dump
命令相同的功能壳。”
获取这样一个迭代器并将基于磁盘的数据库转储到基于内存的数据库中,然后就可以开始计算了。计算完成后,只需以相反的方式转储回磁盘即可。
首先,您应该尝试找出导致您观察到的速度缓慢的原因。你正在写表吗?您的写入是否足够大事务,以便您不会将不必要的临时结果保存到磁盘?您可以更改写入以转到临时表(使用
pragma temp_store=memory
)吗?你能和pragma synchronous=off
一起生活吗?
我不认为这个功能在Python模块中公开,但是sqlite有一个备份API,听起来正是你所要求的:一种从一个数据库复制到另一个数据库的方法(其中一个可能是内存数据库)几乎自动工作,无需任何用户可见的表枚举。 (也许APSW暴露了这一点?)
另一个选择是创建一个 ram 磁盘(如果您对环境有足够的控制)并将文件复制到那里。
如果我们必须使用Python包装器,那么没有比两遍读取和写入解决方案更好的解决方案了。 但从版本 3.7.17 开始,SQLite 可以选择使用内存映射 I/O 直接访问磁盘内容。sqlite mmap
如果你想使用 mmap,你必须使用 C 接口,因为没有包装器提供它。
还有另一种硬件解决方案,内存盘。那么你就有了方便的文件IO和内存的速度。
这个问题之前已经被回答过,包括代码示例:在 python 中,如何在连接到它之前将 sqlite 数据库完全加载到内存?
你没有提到操作系统,但 Windows XP 的一个问题是它默认为 10MB 文件缓存,无论你有多少内存。 (这在系统配备 64MB 等的时代是有意义的)。此消息有多个链接:
这是一种将 SQLite 数据库读入内存的相对简单的方法。根据您在操作数据方面的偏好,您可以使用 Pandas 数据框或将表写入内存中的 sqlite3 数据库。同样,在操作数据后,您可以使用相同的 df.to_sqlite 方法将结果存储回数据库表中。
import sqlite3 as lite
from pandas.io.sql import read_sql
from sqlalchemy import create_engine
engine = create_engine('sqlite://')
c = engine.connect()
conmem = c.connection
con = lite.connect('ait.sqlite', isolation_level=None) #Here is the connection to <ait.sqlite> residing on disk
cur = con.cursor()
sqlx = 'SELECT * FROM Table'
df = read_sql(sqlx, con, coerce_float=True, params=None)
#Read SQLite table into a panda dataframe
df.to_sql(con=conmem, name='Table', if_exists='replace', flavor='sqlite')
使用 Cenk Alti 的解决方案,当进程达到 500MB 时,我总是在使用 Python 3.7 时出现 MemoryError。只有使用 sqlite3 的备份功能(thinwybk 提到的),我才能加载和保存更大的 SQLite 数据库。您也可以通过两种方式仅用 3 行代码完成相同的操作。
当您与
:memory:
sqlite 数据库有多个连接时,例如将 SQLAlchemy 与 source.backup(dest)
函数一起使用时,那么您最终可能不会将备份放入“正确的”内存数据库中。
可以使用正确的连接字符串来修复此问题:https://stackoverflow.com/a/65429612/1617295 并且不涉及任何黑客行为,不使用未记录的功能。
sqlite 支持内存数据库。
在 python 中,您可以使用 :memory: 数据库名称。
也许您可以打开两个数据库(一个来自文件,一个在内存中为空),将文件数据库中的所有内容迁移到内存中,然后进一步使用内存数据库进行计算。