You need to enable JavaScript to run this app. .NET Core中TransactionScope与DistributedLock混用是否合理?
优惠活动
大模型
产品
解决方案
定价
更多

.NET Core中TransactionScope与DistributedLock混用是否合理?

问题核心解答

两种方案混用是完全正确的,二者解决的是不同维度的问题,属于互补关系,没有冲突。但你当前的代码存在执行顺序错误,需要调整。


为什么TransactionScope无法替代分布式锁

你认为「事务执行期间其他机器无法访问同一行」是对SQL Server事务机制的误解:

  • SQL Server默认事务隔离级别为READ COMMITTED,仅会在实际执行写操作时对目标行加排他锁,读操作不加锁,且行锁仅在写操作执行到事务提交的窗口期生效,并不是事务一创建就加锁。
  • 对于createDocument的插入场景,目标行在插入前根本不存在,没有行锁可以提前占用,并发插入请求完全可能同时进入事务逻辑,导致重复插入、数据冲突。
  • 即使你调高事务隔离级别到可重复读、串行化,也只会增加数据库死锁概率,无法从根本上避免多节点的并发竞态问题。

分布式锁和事务的分工

  • 分布式锁:解决多节点并发控制问题,确保同一时间只有一个节点可以操作指定Guid的文档,从根源上避免竞态、脏写、重复插入问题。
  • TransactionScope:解决本地多操作的原子性问题,确保两个存储库的保存操作要么全部生效,要么全部回滚,是数据一致性的最后保障。

你当前代码的问题和修正

你现在的代码把事务创建放在了分布式锁获取之前,这个顺序会导致严重的性能问题:

多个并发请求会先创建事务占用数据库连接,再抢分布式锁,抢不到锁的请求会阻塞在锁获取阶段,长事务长时间占用连接会快速打满数据库连接池,同时大幅提升数据库死锁概率。

正确的执行顺序应该是先抢分布式锁,锁拿到之后再开事务执行业务操作,修正后代码如下:

public void Save(Document document)
{
    // 先拿分布式锁,拿不到直接退出/重试
    using (var lockHandler = documentLock.CreateLockForGuid(document.Guid))
    {
        // 锁获取成功后再开事务
        using var scope = new TransactionScope(TransactionScopeOption.RequiresNew,
                       TransactionScopeAsyncFlowOption.Enabled);
        try
        {
            repositoryOne.Save(document);
            repositoryTwo.Save(document);
            scope.Complete();
        }
        catch (Exception ex)
        {
            // 此处补充异常日志记录,不要吞异常
            throw;
        }
    }
}

额外注意事项

  • 确认你封装的CreateLockForGuid方法获取的是写锁,读写锁的读锁无法阻止写操作,不符合你的业务场景。
  • 分布式锁要设置合理的超时释放时间,避免节点宕机导致锁永久占用。
  • 如果你的Save方法是异步的,要确保TransactionScope的异步流配置生效,同时所有存储库操作都用await调用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.17 11:06:05