【问题标题】:How to avoid concurrent transactions affecting a single entity如何避免影响单个实体的并发事务
【发布时间】:2015-01-08 21:01:41
【问题描述】:

帮助!
情况是两个或多个交易试图影响某个外部系统中的同一个客户货币账户。我需要执行第二个事务,直到第一个事务完成。
考虑:
- 有两个或多个交易试图影响相同的余额
- 同时有多个客户端
- 使用 1000 TPS,每笔交易平均 100 毫秒
想法:
- 因为我们正在使用多线程来支持 1000TPS,所以我正在尝试根据客户端 ID 创建队列。使用某种工作管理器来限制客户端的一个线程。因此,如果我同时有 2 个具有相同 clientID 的请求,则可以动态排队第二个。

工具
我正在尝试使用 Oracle 工具,例如:
- 融合中间件:使用基于消息上下文的 Workmanager [不确定是否可能,因为看起来上下文只能基于会话数据] 我喜欢 WorkManager,因为没有性能问题
- Oracle OCEP:使用 CQL 创建动态队列 [不确定是否可能和性能]
- Oracle 高级队列:也许可以使用事务组。

感谢您的任何想法

【问题讨论】:

  • 最简单的方法——使用数据库固有的行级锁定机制——是否不足?如果货币账户数量足够少,以至于任何两个交易很可能发生冲突,那么主动在线程之间分配交易可能是有意义的。但是,对于一个正常的系统,发生冲突的可能性很小,以至于尝试主动安排任何事情都是没有意义的。
  • 感谢贾斯汀,问题在于我们使用 1K 或 2K TPS 的性能,我们需要快速响应。可能有 500 万笔交易和客户。在解决方案中使用数据库来控制情况的问题是时间和建立连接和更新插入的开销。根据一些经验,我更喜欢找到开箱即用的工具。但我对任何想法都持开放态度
  • 我不关注关于“开销建立连接”的评论。据推测,无论您实施何种解决方案,您都将拥有数据库连接,并且这些物理连接将相对持久。数据库已经提供了开箱即用的自动锁定功能,以防止对单行进行并发修改。您系统中的事务模式是什么让您相信提前检查行锁争用比让线程阻塞存在实际争用更明智?
  • 你是对的,连接是持久的问题是我需要与数据库建立许多连接并在数据库和应用程序服务器之间产生更多的网络流量。但这不是一个大问题。也许我想做的是不要将我的业务解决方案逻辑与多线程争用合并。将核心业务逻辑与行为分开。如果例如 oracle weblogic 已经有解决方案,也许可以避免编码,所以我不必花时间开发一些特定的解决方案。现在我正在使用 Oracle OSB
  • 对于“客户货币账户”的正常工作负载,没有足够的后端争用来抵消尝试提前安排事情的成本——大多数系统都会这样做针对许多不同账户的交易,而不是每秒针对同一个账户的数千笔交易。如果您的系统具有不同的事务模式,值得提前安排事务,那么了解该模式有助于您设计解决方案。

标签: java multithreading oracle weblogic middleware


【解决方案1】:

希望我能解决你的问题。

在您提出的问题中,是否可以在第一个事务完成之前对一行执行第二个事务。这是不可能的!遵循 ACID 范例的数据库必须是C一致的!所以你不能“超越”第一笔交易!!!如果你想这样做,你应该使用一致性不那么强的 NoSQL 数据库(如 MongoDB,...)。

但也许你想知道,如果有一个 Oracle 视图可以弄清楚,一行是否被锁定?让我们假设,有这样的观点。您将检查此视图,如果没有锁定,则开始更新/删除。但是您不能确定这是否会起作用,因为即使在您检查后 1 毫秒,另一个进程也可以锁定它。

您唯一能做的就是在您的 UPDATE/DELETE 语句之前放置一个“select ... for update NOWAIT”。 如果该行被锁定,您将得到一个异常(ORA-00054:资源繁忙)。这是让数据库为您管理行级锁定的推荐/“开箱即用方式”!

请参见以下带有 emp 表的示例。考虑:要检查这一点,请同时在两个不同的会话中启动此代码。

declare
  l_sal number;
  resource_busy exception;                    -- declare your own exception
  pragma exception_init (resource_busy, -54); -- connect your exception with ORA-00054

begin
   select sal
     into l_sal 
     from emp 
     where empno = 7934
   for update NOWAIT;

   update emp
   set sal = sal + 100
   where empno = 7934;

   exception
     when resource_busy then
       null;  -- in your case, simply do nothing, if the row is locked

end;       

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-04
    • 1970-01-01
    相关资源
    最近更新 更多