我在多台机器上使用一个带有peewee
的SQLite数据库,我遇到了各种OperationalError
、DataBaseError
。这显然是多线程的问题,但我根本不是这方面的Maven,也不是SQL方面的Maven。下面是我的设置和我所尝试的。
设置
我使用peewee
记录机器学习实验。基本上,我有多个节点(比如,不同的电脑)运行一个python文件,并且都写到一个共享位置的**base.db
文件。最重要的是,我需要一个从我的笔记本电脑读的访问权限,最多有50个不同的节点示例化数据库并在其上写入内容。
我所尝试的
首先,我使用SQLite对象:
db = pw.SqliteDatabase(None)
# ... Define tables Experiment and Epoch
def init_db(file_name: str):
db.init(file_name)
db.create_tables([Experiment, Epoch], safe=True)
db.close()
def train():
xp = Experiment.create(...)
# Do stuff
with db.atomic():
Epoch.bulk_create(...)
xp.save()
这工作得很好,但我有时会有作业因为数据库被锁定而崩溃。然后,我了解到SQLite只处理每个连接的一个写操作,这就导致了问题。
因此,我转向SqliteQueueDatabase
,因为根据文档,如果“如果您希望从多个线程对SQLite数据库进行简单的读写访问”,它是有用的。
代码如下所示:
db = SqliteQueueDatabase(None, autostart=False, pragmas=[('journal_mode', 'wal')],
use_gevent=False,)
def init_db(file_name: str):
db.init(file_name)
db.start()
db.create_tables([Experiment, Epoch], safe=True)
db.connect()
除了db.atomic
部分之外,保存数据也是一样的。但是,不仅写查询似乎遇到了错误,而且我实际上不再能够访问数据库进行读操作:它几乎总是忙碌的。
我的问题
在这种情况下使用什么对象是合适的?我认为SqliteQueueDatabase
是最合适的。pooled
数据库是更合适的吗?我问这个问题也是因为我不知道我是否很好地掌握了线程部分:从多台机器初始化多个database
对象与在一台机器上使用多个线程(like this situation)初始化一个对象是不同的。
很抱歉,如果这个问题已经在另一个地方回答,并感谢任何帮助!高兴地提供更多的代码,如果需要当然。
3条答案
按热度按时间u5i3ibmn1#
Sqlite一次只支持一个writer,但是当使用WAL模式时,多个reader可以打开数据库(即使writer已经连接)。对于peewee,您可以启用wal模式:
使用多个写入器时,另一个重要的事情是使写入事务尽可能短。https://charlesleifer.com/blog/going-fast-with-sqlite-and-python/(位于“事务、并发和自动提交”标题下)。
还请注意,SqliteQueueDatabase对于 * 单个 * 进程和 * 多个 * 线程运行良好,但如果您有多个进程,它将对您毫无帮助。
a14dhokn2#
在@BoarGules评论之后,我意识到我混淆了两件截然不同的事情:
SqliteQueueDatabase
是一个非常合适值所以我最终安装了Postgre。如果它对我之后的人有用的话,有几个链接,对于linux:
pgloader
导出SQLite数据库。但是,如果你没有合适的库,也不想构建所有的东西,你可以手工完成。我做了下面的事情,不确定是否有更直接的解决方案。1.将您的表导出为csv(遵循@coleifer的注解):
1.在新的Postgre数据库中创建表:
1.使用以下命令将CSV表复制到Postgre db:
其它表也是如此。
它对我来说很好用,因为我只有两张表。如果你有更多的表,可能会很烦人。在这种情况下,如果你能很容易地安装它,
pgloader
似乎是一个非常好的解决方案。更新
一开始我无法从
peewee
创建对象。出现完整性错误:似乎Postgre返回的id
(使用RETURNING 'epoch'.'id'
子句)返回一个已经存在的id。据我所知,这是因为在使用COPY
命令时没有调用增量。因此,它只返回id 1,然后是2,依此类推,直到它到达一个不存在的id。为了避免经历所有这些失败的创建,您可以直接编辑控制RETURN
子句的迭代器,使用:并将
10000
替换为SELECT MAX("id") FROM epoch
,+1中的值。k4aesqcs3#
我认为您可以增加sqlite的超时时间并解决您的问题。
这里的问题是sqlite默认的写操作超时时间很低,即使有少量的并发写操作,sqlite也会抛出异常,这是很常见的,也是众所周知的。
默认值应该是5-10秒。如果超过了这个超时时间,那么要么增加它,要么将你的数据写入数据库。
以下是一个示例:我在这里返回一个
DatabaseProxy
,因为这个代理允许在不更改客户端代码的情况下将sqlite替换为postgres。