You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SQLite内存数据库两阶段提交锁冲突及事务处理疑问

问题描述

我使用SQLite内存数据库做模拟测试,生产环境则基于SQL Server、IBM Db2、Oracle或Sybase这类数据库。当前需要为操作日志构建两阶段提交流程,要求日志与关联的数据库上下文保存同步,部分场景下数据变更和操作日志表位于同一数据库中。

在SQLite内存库中开启事务后,尝试保存context1再保存context2时,约30秒后抛出异常:

SQLite Error 6: 'database table is locked'.

连接字符串:

var connectionString = $"Data Source=file:{DatabaseAlias};mode=memory;cache=shared";
optionsBuilder.UseSqlite(connectionString);

测试代码:

var context1 = factory.CreateDbContext();
var context2 = factory.CreateDbContext();
context1.WithTransaction(() =>
{
   var keysFromContext1 = context1.Keys.ToList();
   keysFromContext1.First().Name = "Changed First";
   context2.WithTransaction(() =>
   {
      var employeesFromContext2 = context2.Employees.ToList();
      employeesFromContext2.First().Name = "Changed Second";
      context1.SaveChanges();
      context2.SaveChanges();
   });
});

事务处理代码:

public void WithTransaction(Action action)
{
   WithTransaction(action, IsolationLevel.ReadUncommitted);
}

public void WithTransaction(Action action, IsolationLevel isolationLevel)
{
   if (Database.CurrentTransaction != null)
   {
      action();
      return;
   }
   Database.BeginTransaction(isolationLevel);
   try
   {
      action();
      Database.CommitTransaction();
   }
   catch
   {
      Database.RollbackTransaction();
      throw;
   }
}

两个上下文操作不同实体,但仍出现锁冲突。我不得不将默认隔离级别改为ReadUncommitted(脏读),否则锁冲突会在context2.Employees.ToList()执行时就触发。

疑问:即使操作不同实体,是否必须提交/回滚已启动的事务才能处理后续新启动的事务?


解答

这本质是SQLite的事务机制特性导致的,与你生产环境使用的数据库存在核心差异:

  1. SQLite采用库级锁机制:不同于SQL Server、Oracle这类支持行级/表级锁的数据库,SQLite的锁是针对整个数据库的——只要某个连接开启了写事务(哪怕只是做了修改准备),整个库会被锁定,其他连接的写操作甚至部分读操作都会被阻塞,直到当前事务提交或回滚。哪怕操作不同实体,也无法绕过这个限制。

  2. 你的代码逻辑放大了冲突:

    • 你在context1的事务中嵌套了context2的事务,两个上下文共享同一SQLite内存库连接(cache=shared仅共享缓存,不共享事务)。
    • context1开启事务后,整个库被锁定,此时context2尝试开启新事务并执行读操作,默认隔离级别下会被阻塞,超时后抛出锁错误。改用ReadUncommitted只是测试环境的临时规避方案,在生产环境这么做会引入脏读风险。
  3. 针对疑问的直接答复:
    在SQLite环境下,是的——必须提交或回滚已启动的事务,才能让其他连接的事务正常执行,哪怕操作的是不同实体。但在你生产使用的SQL Server、Db2等数据库中,由于锁粒度更细,不同实体的事务可以并行执行,无需强制提交前置事务。

  4. 测试环境优化建议:

    • 避免在SQLite中编写跨上下文的嵌套事务逻辑,要么将两个上下文的操作合并到同一个事务中,要么先提交context1的事务再处理context2的操作。
    • 如果必须模拟两阶段提交场景,可考虑为测试用的每个上下文分配独立的SQLite内存库(但可能不符合“同一数据库”的测试需求),或者改用支持细粒度锁的测试数据库(如SQL Server LocalDB)。

内容的提问来源于stack exchange,提问作者Udontknow

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 15:15:03