Nodejs 用 sequelize.js 操作 sqlite3 数据库在并发写事务时,出现 "SQLITE_BUSY" 的报错,但在用 knex.js 时就没有出现这个错误,问题到底出在哪里?
Nodejs 用 sequelize.js 操作 sqlite3 数据库在并发写事务时,出现 “SQLITE_BUSY” 的报错,但在用 knex.js 时就没有出现这个错误,问题到底出在哪里?
虽然我知道 sqlite 数据库本身是不支持并发写事务的,应该是 knex.js 在执行的时候做了相应处理。但我在 knex.js 和 sequelize.js 的文档里都没找到关于 sqlite 该如何处理并发写事务的说明。
所以,让我困惑的问题有两个:
- knex.js 和 sequelize.js 在处理 sqlite 数据库的并发写事务时的区别是什么?
- 如果选择使用 sequelize.js 操作 sqlite 数据库,又该如何处理并发写事务?
有熟悉 sequelize 和 knex.js 这两个库的 v 友吗?
下面是部分代码段,为了方便重现报错,我写了一个小 demo,https://github.com/Watanuki-Kimihiro/sqlite-busy
byKnex
// sqlite-busy/byKnex/db.js
const insertWorkMetadata = work => knex.transaction(trx => trx.raw(
// insert or ignore into `t_circle` (`id`, `name`) values (12345, 'Circile_Name')
trx('t_circle')
.insert({
id: work.circle.id,
name: work.circle.name,
}).toString().replace('insert', 'insert or ignore'),
)
.then(() => trx('t_work')
.insert({
id: work.id,
title: work.title,
circle_id: work.circle.id
})));
createSchema()
.then(() => {
let work = {
id: 123456,
title: ‘Work_Title’,
circle: {id:12345, name:‘Circile_Name’}
};
let promises = [];
for (let i=1; i<10; i++) {
let newWork = JSON.parse(JSON.stringify(work));
newWork.id = work.id + i;
promises.push(insertWorkMetadata(newWork));
}
return Promise.all(promises);
})
.then(() => {console.log(“finished”)})
.catch((err) => {
console.error(err);
});
bySequelize
// sqlite-busy/bySequelize/db.js
const insertWorkMetadata = work => sequelize.transaction(t => {
return Circle.findOrCreate({
where: {id: work.circle.id},
defaults: {name: work.circle.name},
transaction: t
})
.then(([circle, created])=> {
return circle.createWork({
id: work.id,
title: work.title,
}, {transaction: t});
});
});
sequelize.sync()
.then(() => {
let work = {
id: 123456,
title: ‘Work_Title’,
circle: {id:12345, name:‘Circile_Name’}
};
let promises = [];
for (let i=1; i<10; i++) {
let newWork = JSON.parse(JSON.stringify(work));
newWork.id = work.id + i;
promises.push(insertWorkMetadata(newWork));
}
return Promise.all(promises);
})
.then(() => {console.log(“finished”)})
.catch((err) => {
console.error(err);
});
在Node.js中使用Sequelize.js操作SQLite3数据库时遇到“SQLITE_BUSY”错误,通常是由于SQLite数据库的锁定机制导致的。SQLite是一个轻量级的数据库,它在处理并发写操作时使用的是文件锁来保证数据的一致性。当多个事务试图同时写入数据库时,SQLite会触发锁等待,如果等待时间过长,就会抛出“SQLITE_BUSY”错误。
相比之下,Knex.js在处理SQLite时可能没有触发同样的错误,这可能是因为Knex.js的事务管理或查询队列处理机制与Sequelize.js有所不同。Knex.js可能更高效地管理并发,或者在遇到锁等待时采取了不同的策略(如重试机制)。
为了解决这个问题,你可以尝试以下几个方法:
-
增加数据库锁等待时间:
const sequelize = new Sequelize('database', 'username', 'password', { dialect: 'sqlite', storage: 'path/to/database.sqlite', pool: { max: 5, min: 0, acquire: 30000, // 增加获取连接超时时间 idle: 10000 }, dialectOptions: { busyTimeout: 5000 // 设置SQLite忙等待超时时间 } });
-
优化事务处理逻辑:确保事务尽可能短,减少锁的持有时间。
-
使用数据库连接池:合理配置连接池,避免连接耗尽。
如果问题依旧存在,可能需要考虑使用更适合高并发场景的数据库,如MySQL或PostgreSQL。