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

Delphi:多客户端环境下如何实现乐观锁?

针对Delphi FireDAC + Postgres遗留ERP的并发控制解决方案

现有方案的优化思路

1. 互斥锁(MUTEX)的优化

你当前的数据库互斥锁扩展性差,但可以做针对性优化:

  • 缩小锁粒度:不要给整个方法加锁,只锁定关键数据行,比如操作库存时仅锁定对应物料ID的行,而非全表或整个模块。Postgres可通过SELECT ... FOR UPDATE SKIP LOCKED实现行级锁,避免无意义等待。
  • 动态调整重试策略:根据并发情况调整重试间隔和次数,比如初始间隔100ms,重试2次后间隔翻倍,避免并发风暴。

2. 版本控制(乐观锁)的轻量化改造

全量改造应用成本高,但可分步推进:

  • 先给高并发核心表(如物料库存、采购单)添加version字段,用触发器自动维护版本号,无需修改所有更新逻辑,仅在关键操作后检查受影响行数。
  • 利用FireDAC的TFDCommand.RowsAffected属性快速判断更新是否生效,封装通用检查函数减少重复代码。

其他可行方案

1. Postgres行级锁结合FireDAC事务配置

FireDAC原生支持通过事务隔离级别和锁语句实现并发控制,具体配置方式:

  • 设置事务隔离级别:将TFDConnection.Transaction.IsolationLevel设为ilReadCommitted(Postgres默认)或ilRepeatableRead,避免脏读和不可重复读。
  • 显式行级锁:查询数据时直接加锁,示例SQL:
    SELECT * FROM material_stock WHERE id = :MatID FOR UPDATE;
    
    执行该查询后,其他会话操作该行会被阻塞,直到当前事务提交/回滚。若不想阻塞,可改用FOR UPDATE SKIP LOCKED跳过已锁定行,适合批量操作场景。
  • FireDAC事务配置:开启TFDTransaction.AutoCommit为False,操作完成后手动Commit,异常时Rollback,确保锁的范围在事务内。

2. 利用Postgres的SERIALIZABLE隔离级别

这是Postgres最强隔离级别,会自动检测并发冲突并回滚冲突事务,应用层只需捕获40001错误码(序列化失败)并重试即可。该方案无需修改表结构,只需:

  • 在FireDAC中设置事务隔离级别为ilSerializable
  • 封装通用事务重试逻辑,示例Delphi代码:
    function ExecuteWithRetry(ADACall: TFunc<Boolean>): Boolean;
    var
      RetryCount: Integer;
    begin
      RetryCount := 0;
      Result := False;
      while RetryCount < 3 do
      begin
        try
          FDConnection.StartTransaction;
          Result := ADACall();
          FDConnection.Commit;
          Break;
        except
          on E: EFDDBEngineException do
          begin
            FDConnection.Rollback;
            // 捕获Postgres序列化失败错误码
            if E.ErrorCode = 40001 then
            begin
              Inc(RetryCount);
              Sleep(100 * RetryCount);
            end
            else
              Raise;
          end;
        end;
      end;
    end;
    
    该方案对应用侵入性极低,适合遗留系统快速改造,但需注意高并发下重试次数过多的问题,可根据业务调整重试阈值。

3. 基于Postgres Advisory Locks(咨询锁)

咨询锁是数据库级别的自定义锁,可针对业务逻辑加锁,比如根据物料ID生成锁键:

-- 获取锁,超时5秒
SELECT pg_advisory_lock(:MatID);
-- 释放锁
SELECT pg_advisory_unlock(:MatID);

FireDAC可直接执行这些SQL,相比表级锁更灵活,锁粒度可自定义,且不会阻塞无关操作,适合采购单审批等特定业务场景。

方案选型建议

  • 若追求最小侵入性:优先选SERIALIZABLE隔离级别+重试逻辑,无需改表,仅需封装事务重试。
  • 若核心表并发极高:采用乐观锁(version字段)+行级锁结合,分步改造核心模块。
  • 若特定业务逻辑需自定义锁:使用Postgres咨询锁+FireDAC事务控制。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 13:17:18