35

最近在学习使用 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();
4

2 回答 2

40

Each command inside the serialize() function is guaranteed to finish executing before the next one starts.

In your example, the CREATE TABLE will finish before the INSERT gets run. If you didn't use serialize() then the CREATE TABLE and INSERT statements would be run in parallel. They would start so quickly one after the other that the INSERT may actually finish before the table has been created, giving you an error about trying to insert data into a table that doesn't exist.

This is called a race condition, because every time you run your program you might get a different winner. If CREATE TABLE wins the race then the program will work fine. But if INSERT wins the race, the program will break with an error. Since you can't control who wins the race, serialize() will stop INSERT from even starting until CREATE TABLE has reached the end, ensuring you get the same outcome every time.

In your second example with only one statement then serialize() is still required. This is because run() starts the SQL query but returns immediately, leaving the query to run in the background. Since your very next command is one to close() the database, you'll cut it off while the query is still running.

Since serialize() doesn't return until the last of its internal queries has completed, using it will hold off the close() until the query has completed.

If you were using a different type of query (say in response to a user clicking a button on a web page, where the database is left open between calls) then you probably wouldn't need serialize(). It just depends whether the code that follows each query requires that the queries before it have completed or not.

When deciding whether to use serialize() or not, it can be helpful to think of any non-serialized queries as if they are commented out, and then see if the code would still work. In your first example above, removing the CREATE TABLE command would break the following INSERT statement (because then there'd be no table to insert into), therefore these need to be serialised. But if you had two CREATE TABLE commands then removing one would not affect the other, so those two commands would not have to be serialized.

(This tip doesn't apply to close() however - the rule of thumb there is to only call close() once everything has finished running.)

于 2017-03-22T08:36:12.350 回答
4

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

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

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

于 2018-08-28T04:24:32.883 回答