Nodejs 用 sequelize.js 操作 sqlite3 数据库在并发写事务时,出现 "SQLITE_BUSY" 的报错,但在用 knex.js 时就没有出现这个错误,问题到底出在哪里?

发布于 1周前 作者 gougou168 来自 nodejs/Nestjs

Nodejs 用 sequelize.js 操作 sqlite3 数据库在并发写事务时,出现 “SQLITE_BUSY” 的报错,但在用 knex.js 时就没有出现这个错误,问题到底出在哪里?

虽然我知道 sqlite 数据库本身是不支持并发写事务的,应该是 knex.js 在执行的时候做了相应处理。但我在 knex.js 和 sequelize.js 的文档里都没找到关于 sqlite 该如何处理并发写事务的说明。

所以,让我困惑的问题有两个:

  1. knex.js 和 sequelize.js 在处理 sqlite 数据库的并发写事务时的区别是什么?
  2. 如果选择使用 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); });


1 回复

在Node.js中使用Sequelize.js操作SQLite3数据库时遇到“SQLITE_BUSY”错误,通常是由于SQLite数据库的锁定机制导致的。SQLite是一个轻量级的数据库,它在处理并发写操作时使用的是文件锁来保证数据的一致性。当多个事务试图同时写入数据库时,SQLite会触发锁等待,如果等待时间过长,就会抛出“SQLITE_BUSY”错误。

相比之下,Knex.js在处理SQLite时可能没有触发同样的错误,这可能是因为Knex.js的事务管理或查询队列处理机制与Sequelize.js有所不同。Knex.js可能更高效地管理并发,或者在遇到锁等待时采取了不同的策略(如重试机制)。

为了解决这个问题,你可以尝试以下几个方法:

  1. 增加数据库锁等待时间

    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忙等待超时时间
      }
    });
    
  2. 优化事务处理逻辑:确保事务尽可能短,减少锁的持有时间。

  3. 使用数据库连接池:合理配置连接池,避免连接耗尽。

如果问题依旧存在,可能需要考虑使用更适合高并发场景的数据库,如MySQL或PostgreSQL。

回到顶部