问题:
在Python(3.6)中使用内置的SQLite3数据库时,对一个非常大的数据库表进行迭代并逐行更新的注意事项是什么?
要求:
我将需要迭代每一行,并使用列中的信息来执行操作,然后使用结果更新该行的第二列。我无法即时执行此操作,我需要完全更新表格。
此外,根据数据库的大小以及执行每个操作所需的时间,此操作预计将持续多天。考虑到这一点,它必须具有容错能力,能够定期提交更改。
问题
我首先要研究不同的提取方法,但我想知道不同方法对内存的考虑。我见过另一台服务器上的外部数据库,当您执行fetchmany()
时,数据库会生成带有所有结果的select语句,但一次仅返回arraysize
个结果-是这种情况与SQLite以及?如果是这样,使用fetchmany()
vs fetchall()
vs fetchone()
有什么好处吗?
我计划与executemany()
一起使用生成器函数,以便更新数据库并定期提交,假设我可以分块获取以便间歇性中断生成器函数以使其提交。
示例:
table:
columns: id, value1, updated_value
def action_function():
next_id = next_row()
updated_value = compute_value(id)
yield (updated_value, next_id)
def next_row():
while True:
results = cur.fetchmany(arraysize)
if not results:
break
for result in results:
yield result[0]
cur.executemany('''UPDATE table SET updated_value = ? WHERE id = ?''', action_function())
这应该是什么样子吗?我认为可能应该颠倒函数,以使分块提交每个arraysize
?
最后,数据库有数百万行,需要计算每行一个操作(使用同一行中的值),然后更新一列。将数据库加载到内存可能无法正常工作,因此fetchall()
似乎很不错,但是考虑到标准SQL数据库在SELECT
语句将结果加载到内存的情况下如何工作,Python中的SQLite3会发生这种情况吗?如果没有,那是怎么回事?
是否应该有更好的方法,例如简单地为每个块保留start_rowid和stop_rowid的变量,然后计算下一个块的大小,并为这些索引执行SELECT
语句?
感谢您的帮助!
编辑:
作为替代选择,有create_function
逐行显示。
类似这样的东西:
con.create_function("action", 1, compute_value)
cur.execute("UPDATE table SET updated_value = action(id)")
This answer建议使用此方法,但考虑到它至少必须具有一定的容错能力,所以我认为该方法不会起作用,并且仅由于以下原因,完成时间仍然很大: compute_value
做事情的时间。
如果我使用此方法并且过程被中断,那么到目前为止计算出的值会被提交到数据库还是会丢失?
答案 0 :(得分:1)
假设无法在数据库上完成compute_value()
,我将使其保持简单:
SELECT
-全部。然后fetchone()
,运行compute_value()
和UPDATE
那一行。您的问题听起来很像compute_value()
,这是您大部分时间都在输的地方。一次读取并提交一行可能不会增加太多开销,并且将对内存的影响最小化。它还带来了额外的好处,即您总是在每个compute_value()
之后提交。如果您认为这样,我将使用fetchmany()
切换到窗口化方法。
研究如何在行基础上并行执行compute_value()
可能对您而言很有意义。将multiprocessing与您的行是作业的作业队列一起使用会很有益。
答案 1 :(得分:1)
最好分批执行任务:
它将大大减少IO进出数据库的时间
答案 2 :(得分:1)
我首先要查看不同的提取方法,但我想知道 不同方法对内存的考虑是什么。我有 当您执行其他操作时,可以看到另一台服务器上的外部数据库 fetchmany()数据库正在生成所有的select语句 您的结果,但一次仅返回arraysize结果-是 SQLite也是如此吗?如果是这样,有什么好处吗? 使用fetchmany()vs fetchall()vs fetchone()吗?
首先,SQLite Docs讨论查询在SQLite后端的工作方式。概括起来,一旦启动SELECT语句,就准备好sqlite3_stmt
。此处包含检索结果的说明。要获得结果的下一行,将调用sqlite3_step()
直到下一个结果行准备就绪。
因此可以确定,在Python界面上,当您执行fetchone()
时,它将运行step()
一次。当您执行fetchmany()
时,它会循环遍历step()
直到结果量等于您的数组大小属性,然后将它们放入Python列表对象中。 fetchall()
一直循环播放,直到没有结果可言为止,并再次使它们成为Python列表对象。因此,内存是有好处的,因为基于结果的数量,结果列表对象的大小将有所不同。您的光标对象将始终为静态大小。
有没有更好的方法,例如简单地按住 每个块的start_rowid和stop_rowid变量,然后 计算下一个块的大小,并只为 那些索引?
执行此方法将花费更多时间来关闭语句,然后准备下一条语句,而不是执行一次SELECT
然后一次使用fetchmany()
来检索数据块< / p>
就本示例的其余部分而言,我决定采用一种多处理方法(由Ente建议),以充分利用资源,并将与SELECT
一起使用单个fetchone()
添加到队列中。我将有多个工作进程从Queue中提取并发送API调用,然后将结果添加到DoneQueue中。我将通过DoneQueue进行最后的过程分块,然后对executemany()
进行UPDATE
。