我有一个长期运行的多行更新,例如:
UPDATE T set C1 = calculation(C2) where C1 is NULL
如果表格很大,此更新可能需要几秒钟甚至几分钟。 在此期间,此表上的所有其他查询都失败并显示“数据库已锁定” 连接超时到期后(目前我的超时为5秒)。
我想在3秒之后停止此更新查询, 然后重新启动它。希望在几次重启后,整个表格都会更新。 另一个选择是在发出任何其他请求之前停止此更新查询 (这将需要进程间合作,但可能是可行的)。
但我无法找到一种方法来停止更新查询而不回滚 以前更新的所有记录。 我试过调用interrupt并从progress_handler返回非0。 这两种方法都会中止update命令 并回滚所有更改。 因此,似乎sqlite将此更新视为事务, 在这种情况下没有多大意义,因为所有行都是独立的。 但我无法为每一行开始新的交易,可以吗?
如果中断和progress_handler无法帮助我,我还能做些什么?
我还尝试使用LIMIT UPDATE和WHERE custom_condition(C1)。 这些方法允许我提前终止更新, 但它们明显慢于常规更新 并且他们无法在特定时间终止查询 (在另一个连接超时到期之前)。
还有其他想法吗? 这种多行更新是一种常见的操作 我希望,其他人有一个很好的解决方案。
答案 0 :(得分:3)
因此,似乎sqlite将此更新视为事务,在这种情况下没有多大意义,因为所有行都是独立的。
不,这实际上非常有意义,因为您没有执行多个独立更新。您正在执行单个更新语句。 fine manual说
除了在事务中,不能对数据库进行任何更改。 任何更改数据库的命令(基本上都是任何SQL命令 除SELECT之外,如果有的话,将自动启动一个事务 尚未生效。自动启动的交易是 在最后一个查询完成时提交。
如果您可以确定所涉及的密钥范围,则可以执行多个更新语句。例如,如果键是一个整数,并且您确定范围是1到1,000,000,则可以编写代码来执行这一系列更新。
begin transaction;
UPDATE T set C1 = calculation(C2)
where C1 is NULL and your_key between 1 and 100000;
commit;
begin transaction;
UPDATE T set C1 = calculation(C2)
where C1 is NULL and your_key between 100001 and 200000;
commit;
其他可能性。 。
根据我的经验,以这种方式处理更新是不寻常的,但听起来它适合您的应用程序。
但我无法为每一行开始新的交易,可以吗?
嗯,你可以,但可能无法提供帮助。它与上面的方法基本相同,使用单个键而不是范围。不过,我不会解雇你进行测试。
在我的桌面上,我可以在1.455秒内插入100k行,并在420毫秒内通过简单计算更新100k行。如果您在手机上运行,那可能不相关。
答案 1 :(得分:0)
你提到LIMIT表现不佳。你有一个带有索引的lastupdated列吗?在您的程序的顶部,您将获得COMMENCED_DATETIME并将其用于运行中的每个批次:
update foo
set myvalue = 'x', lastupdated = UPDATE_COMMENCED
where id in
(
select id from foo where lastupdated < UPDATE_COMMENCED
limit SOME_REASONABLE_NUMBER
)
P.S。关于缓慢:
I also tried UPDATE with LIMIT and also WHERE custom_condition(C1). These approaches do allow me to terminate update earlier, but they are significantly slower than regular update...
如果您愿意让其他进程访问陈旧数据,并且您的更新旨在不占用系统资源,为什么需要在一定时间内完成更新?似乎没有必要担心绝对术语中的性能。关注点应该是相对到其他进程 - 确保它们没有被阻止。
答案 2 :(得分:0)
我也发了这个问题 http://thread.gmane.org/gmane.comp.db.sqlite.general/81946 并获得了几个有趣的答案,例如:
将rowid的范围划分为切片并一次更新一个切片
使用AUTOINCREMENT功能在上次更新结束的地方开始新的更新(通过LIMIT 10000)
创建一个触发器,调用select raise(fail,...)以中止更新而不回滚