4

我正在使用 Python 2.7 和 SQLite。我正在构建一个包含数百万行的数据库。我只想偶尔写入磁盘,这样可以提高性能。我的想法是只不时调用 commit() 。我已经用下面的代码试过了。中间的选择表明我们得到了一致的读取。但是,当我查看光盘时,我看到了一个文件example.db-journal。这必须是缓存数据的位置。在这种情况下,就性能而言,这对我没有任何好处。有没有办法让插入在内存中收集,然后将它们刷新到磁盘?有一个更好的方法吗?

这是我的示例代码:

import sqlite3
conn = sqlite3.connect('example.db')
c = conn.cursor()
c.execute('CREATE TABLE if not exists stocks (date text, trans text, symbol text, qty real, price real)')

c.execute("INSERT INTO stocks VALUES ('2006-01-05','BUY','RHAT',100,35.14)")
c.execute("INSERT INTO stocks VALUES ('2006-01-05','BUY','RHAT',100,35.14)")
t = ('RHAT',)
c.execute('SELECT date, symbol, trans FROM stocks WHERE symbol=?', t)
# Here, we get 2 rows as expected.
print c.fetchall()
c.execute("INSERT INTO stocks VALUES ('2006-01-05','BUY','RHAT',100,35.14)")
c.execute("INSERT INTO stocks VALUES ('2006-01-05','BUY','RHAT',100,35.14)")
c.execute("INSERT INTO stocks VALUES ('2006-01-05','BUY','RHAT',100,35.14)")
c.execute("INSERT INTO stocks VALUES ('2006-01-05','BUY','RHAT',100,35.14)")
c.execute("INSERT INTO stocks VALUES ('2006-01-05','BUY','RHAT',100,35.14)")

conn.commit()

t = ('RHAT',)
c.execute('SELECT date, symbol, trans FROM stocks WHERE symbol=?', t)
# Here, we get all the rows as expected.
print c.fetchall()

conn.close()

更新:

想我会用一些代码更新,以防有人遇到这个问题。我正在处理来自文本文件的 5+ 百万行,并且需要一个地方来存储数据以进行更多处理。我最初将所有数据都保存在内存中,但是内存不足。所以,我切换到 SQLite 来进行磁盘缓存。我的原始内存版本处理从原始文本文件中每 50,000 行花费了大约 36 秒。

测量后,我对批处理的 SQLite 版本的第一次剪辑花了大约 660 秒处理 50,000 行。根据评论(感谢张贴者),我想出了以下代码:

self.conn = sqlite3.connect('myDB.db', isolation_level='Exclusive')
self.cursor.execute('PRAGMA synchronous = 0')
self.cursor.execute('PRAGMA journal_mode = OFF') 

此外,我在处理我的文本文件中的 1000 行后提交。

if lineNum % 1000 == 0:
    self.conn.commit()

这样,文本文件中的 50,000 行现在需要大约 40 秒。所以,我在总时间上增加了 11%,但是,内存是恒定的,这更重要。

4

2 回答 2

3

首先,你确定你需要这个吗?对于读取,操作系统无论如何都应该缓存文件,如果你写了很多,不同步到磁盘意味着你很容易丢失数据。

如果您测量并将其识别为瓶颈,则可以使用内存数据库connect(':memory:')并获取迭代器,按需返回 sql 转储:http: //docs.python.org/2/library/sqlite3.html#sqlite3。连接.iterdump

import sqlite3, os

in_memory = sqlite3.connect(':memory:')
# do stuff

con = sqlite3.connect('existing_db.db')
con.execute('drop table stocks')
for line in in_memory.iterdump():
    con.execute(line)

再一次,测量你是否需要这个。如果您有足够重要的数据,请认真考虑使用不同的数据存储,例如像 postgres 这样的成熟 DBMS。

于 2013-05-15T18:40:05.067 回答
1

在您的情况下,您正在以自动提交模式创建一个 db-connection,这意味着每次执行INSERT语句时,数据库都会启动一个事务,执行该语句并提交。所以你commit的 - 在这种情况下 - 毫无意义。请参阅 python 文档上的 sqlite3

但是您是正确的,理想情况下应该在事务中完成插入大量行。这向连接发出信号,它应该INSERT在日志文件中记录所有传入的语句,但延迟写入数据库文件直到事务提交。即使您的执行受到 I/O 操作的限制,写入日志文件也不会造成严重的性能损失。

于 2013-05-15T19:00:09.200 回答