node.js sqlite 事务隔离
node.js sqlite transaction isolation
如何确保 SQLite 不会交错来自多个并发节点的查询。js/Express HTTP 请求到单个事务中?
我希望来自不同请求的数据库查询在单独的事务中执行,彼此隔离,允许每个事务彼此独立地提交或回滚。考虑到 node.js 的单线程(和单数据库连接?)特征,这似乎特别有问题。
我一直在浏览数十个网页和文档,但没有找到任何(对我而言)关于如何(或是否)处理这种情况的明确解释。例如 db.serialize() 的描述只说明一次将执行一个查询 - 它没有说明属于不同事务的查询的分离。
感谢任何指点!
我遇到了完全相同的问题。这是我解决它的方法:
第 1 步:确保所有数据库访问都是通过一组库函数完成的,其中数据库作为第一个输入参数传入。
步骤 2:创建数据库对象时(使用 new sqlite3.Database('path')
),用一些额外的元信息包装对象:
这是我的包裹方式:
var wrap = { db_obj : new sqlite3.Database('path'), locked : "UNLOCKED", work_queue : [] }
步骤 3:创建将用于 运行 数据库函数的函数。如果数据库被锁定,这个函数将推迟它们的执行。我打电话给我 "runAsync":
function runAsync( db, fn, callBack) {
if (db.locked === "LOCKED" ) {
db.work_queue.push( function() { runAsync( db, fn, callBack ); });
return;
}
fn(db,callBack);
}
我们可以看到这个函数检查了包装的db对象的状态。如果它被锁定,我们通过将函数存储在稍后执行的 "work queue" 中来延迟执行(通过执行 fn()
)。
第 4 步:创建将用于 运行 数据库函数 独占 的函数。如果数据库被锁定,这个函数将延迟执行,否则它会锁定数据库,运行它的函数然后解锁数据库。我打电话给我:runAsyncExclusive()
:
function runAsyncExclusive( db, fn, callBack ) {
if(db.locked === "LOCKED") {
db.work_queue.push( function() { runAsyncExclusive( db, fn, callBack) });
return;
}
var exclusiveDb = {
db_obj : db.db_obj,
locked : "UNLOCKED",
work_queue : []
};
db.locked = "LOCKED";
fn(exclusiveDb, function(err,res) {
db.locked = "UNLOCKED";
var workItems = db.work_queue;
_.each(workItems, function(fn) { fn(); });
db.work_queue = [];
callBack(err,res);
})
}
传递给函数的 exclusiveDb 对象将允许对数据库进行独占访问。该对象本身可以被锁定,允许任意深度嵌套锁定。
第 5 步:调整您的库函数,使其在适当的地方调用 asyncRun()
和 asyncRunExclusive()
:
function get(db,sql,params,callBack) {
db.get(sql,params,callBack);
}
变成...
function get(db,sql,params,callBack) {
runAsync( db, function(db,cb) { db.get(sql,params,cb); }, callBack );
}
瞧!
(如果不够清楚,请见谅)
如何确保 SQLite 不会交错来自多个并发节点的查询。js/Express HTTP 请求到单个事务中?
我希望来自不同请求的数据库查询在单独的事务中执行,彼此隔离,允许每个事务彼此独立地提交或回滚。考虑到 node.js 的单线程(和单数据库连接?)特征,这似乎特别有问题。
我一直在浏览数十个网页和文档,但没有找到任何(对我而言)关于如何(或是否)处理这种情况的明确解释。例如 db.serialize() 的描述只说明一次将执行一个查询 - 它没有说明属于不同事务的查询的分离。
感谢任何指点!
我遇到了完全相同的问题。这是我解决它的方法:
第 1 步:确保所有数据库访问都是通过一组库函数完成的,其中数据库作为第一个输入参数传入。
步骤 2:创建数据库对象时(使用 new sqlite3.Database('path')
),用一些额外的元信息包装对象:
这是我的包裹方式:
var wrap = { db_obj : new sqlite3.Database('path'), locked : "UNLOCKED", work_queue : [] }
步骤 3:创建将用于 运行 数据库函数的函数。如果数据库被锁定,这个函数将推迟它们的执行。我打电话给我 "runAsync":
function runAsync( db, fn, callBack) {
if (db.locked === "LOCKED" ) {
db.work_queue.push( function() { runAsync( db, fn, callBack ); });
return;
}
fn(db,callBack);
}
我们可以看到这个函数检查了包装的db对象的状态。如果它被锁定,我们通过将函数存储在稍后执行的 "work queue" 中来延迟执行(通过执行 fn()
)。
第 4 步:创建将用于 运行 数据库函数 独占 的函数。如果数据库被锁定,这个函数将延迟执行,否则它会锁定数据库,运行它的函数然后解锁数据库。我打电话给我:runAsyncExclusive()
:
function runAsyncExclusive( db, fn, callBack ) {
if(db.locked === "LOCKED") {
db.work_queue.push( function() { runAsyncExclusive( db, fn, callBack) });
return;
}
var exclusiveDb = {
db_obj : db.db_obj,
locked : "UNLOCKED",
work_queue : []
};
db.locked = "LOCKED";
fn(exclusiveDb, function(err,res) {
db.locked = "UNLOCKED";
var workItems = db.work_queue;
_.each(workItems, function(fn) { fn(); });
db.work_queue = [];
callBack(err,res);
})
}
传递给函数的 exclusiveDb 对象将允许对数据库进行独占访问。该对象本身可以被锁定,允许任意深度嵌套锁定。
第 5 步:调整您的库函数,使其在适当的地方调用 asyncRun()
和 asyncRunExclusive()
:
function get(db,sql,params,callBack) {
db.get(sql,params,callBack);
}
变成...
function get(db,sql,params,callBack) {
runAsync( db, function(db,cb) { db.get(sql,params,cb); }, callBack );
}
瞧!
(如果不够清楚,请见谅)