`node-sqlite3`中的`db.serialize`是如何工作的?

45

最近我正在学习使用node和node-sqlite3来操作sqlite3数据库,这里是一个示例。

var sqlite3 = require('sqlite3');
var db = new sqlite3.Database(':memory:');
db.serialize(function() {
    db.run("CREATE TABLE test(info TEXT)");
    db.run("INSERT INTO test (info) VALUES ('info1')");
})
db.close();

文档说明说db.serialized用于确保SQL语句按顺序执行,但我感到困惑,如果没有db.serialize,它们不会按顺序执行吗?毕竟它们将从事件队列中提取并按顺序执行。这里是如何工作的?

如果只有一个要执行的SQL语句,是否可以安全地在没有db.serialize的情况下运行它,如下所示?

var sqlite3 = require('sqlite3');
var db = new sqlite3.Database(':memory:');
db.run("CREATE TABLE test(info TEXT)");
db.close();

可能其中一个原因:https://dev59.com/DmMk5IYBdhLWcg3w3xlY#18899872 - 3AK
2个回答

52

serialize()函数中的每个命令都保证在下一个命令开始之前完成执行。

在你的例子中,CREATE TABLE会在INSERT运行之前完成。如果你不使用serialize(),那么CREATE TABLEINSERT语句将并行运行。它们会非常快地连续启动,以至于INSERT可能实际上在表格创建之前就完成了,导致您尝试向不存在的表格插入数据出现错误。

这被称为竞争条件,因为每次运行程序时,可能会得到不同的结果。如果CREATE TABLE赢得比赛,那么程序将正常工作。但是,如果INSERT赢得比赛,则程序将出现错误。由于无法控制谁赢得比赛,serialize()将阻止INSERT甚至开始,直到CREATE TABLE到达末尾,确保您每次都获得相同的结果。

在只有一个语句的第二个例子中,仍然需要serialize()。这是因为run()启动SQL查询但立即返回,使查询在后台运行。由于紧接着的下一个命令是close()数据库,您将在查询仍在运行时截断它。

由于serialize()不返回直到其内部查询的最后一个已完成,使用它将推迟close()直到查询完成。

如果您正在使用不同类型的查询(例如响应用户在网页上单击按钮时,在调用之间保留数据库打开),则可能不需要serialize()。这只取决于每个查询后面的代码是否需要先前的查询已完成。

在决定是否使用serialize()时,可以将任何未序列化的查询视为已注释掉,然后查看代码是否仍然有效。在上面的第一个示例中,删除CREATE TABLE命令将破坏以下的INSERT语句(因为那里没有表可插入),因此这些需要被序列化。但是,如果您有两个CREATE TABLE命令,那么删除其中一个不会影响另一个,因此这两个命令就不必被序列化。

(这个提示不适用于close() - 在那里的经验法则是只有当所有事情都运行完毕时才调用close()。)


有没有一种方法可以保证在回调被调用之前完成run()调用?我正在使用带有保存点的序列化,但间歇性地回调返回,但我的序列化查询尚未运行,我看到我的代码在它们运行之前调用数据库上的close。这是一个大问题,因为我试图使用从这些调用返回的数据。 - Michael
@Michael:不确定你所说的run()具有回调的含义,但听起来这应该是一个新问题,而不是评论,这样你可以提供更多细节。 - Malvineous

7
我在SQLite文档中找到了这个内容:

Database#close方法总是以独占模式运行,这意味着它会等待所有先前的查询完成,而node-sqlite3在关闭挂起时不会运行任何其他查询。

因此,看起来你最后一个问题的答案是。如果您只有一个要运行的查询,则无需使用serialize函数。 您不必担心在查询完成之前关闭数据库,因为SQLite足够聪明,不会这样做! :)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接