当我尝试使用多线程保留对象时遇到问题。
详细信息:
假设我有一个对象 PaymentOrder
其中有一个列表 PaymentGroup
(一对多关系)和 PaymentGroup
包含 CreditTransfer
的列表(再次是一对多关系)。
自数量CreditTransfer
很大(十万),我根据 PaymentGroup
对它进行分组(基于一些业务逻辑)
并创建 WORKER 线程(每个 PaymentGroup 一个线程)以形成 PaymentOrder
对象并提交到数据库中。
问题是,每个工作线程都创建一个PaymentOrder
(其中包含一组唯一的 PaymentGroup
)。
所有实体的主键都是自动生成的。
所以有三个表,1. PAYMENT_ORDER_MASTER、2. PAYMENT_GROUPS、3. CREDIT_TRANSFERS,都是通过一对多关系映射的。
因为当第二个线程尝试将其组保留在数据库中时,框架会尝试保留相同的 PaymentOrder
,前一个线程提交,事务由于一些其他唯一字段约束( PaymentOrder
的校验和)而失败。
理想情况下,它必须是 1..n..m ( PaymentOrder
->PaymentGroup -->
CreditTransfer`)
我需要实现的是如果没有PaymentOrder
的条目在数据库中输入一个条目,如果有,则不要在 PAYMENT_ORDER_MASTER
中输入条目,但仅限于PAYMENT_GROUPS
和CREDIT_TRANSFERS
.
如何解决这个问题,维护 split-master- payment-order-using-groups 逻辑和多线程?
最佳答案
您有多种选择。
1)原始但简单,在最后捕获关键违规错误并在没有 parent 的情况下重试插入。假设你的 parent 确实是独一无二的,你知道另一个线程只是让 parent ……继续照顾 child 。与其他选项相比,这可能表现不佳,但也许您会得到所需的流行。如果您有一个 child 的 parent 比例较高,那么效果会很好。
2) 更改读取一致性级别。它是特定于供应商的,但有时您可以读取未提交的事务。这将帮助您在提交之前查看其他线程的工作。这并不是万无一失的,你仍然必须执行#1,因为另一个线程可以在读取后潜入。但它可能会提高您的吞吐量,但代价是更加复杂。基于 RDBMS 可能是不可能的(或者也许它可能发生,但仅在数据库级别,弄乱其他应用程序!)
3) 使用单线程消费者实现工作队列。如果程序的主要昂贵工作是在持久性级别之前,您可以让线程将其数据“插入”到工作队列中,其中不强制执行键。然后从工作队列中提取一个线程并保留。工作队列可以位于内存中、另一个表中或供应商特定的位置(Weblogic 队列、Oracle AQ 等)。如果程序的主要工作是在持久化之前,那么您可以并行化它并返回到插入上的单个线程。您甚至可以让您的消费者在“批量插入”模式下工作。亲爱的。
4)放松你的限制。谁真正关心同一个 child 的两个 parent 是否持有相同的信息?我只是问问。如果您以后不需要对家长信息进行超快速更新,并且可以更改阅读程序来理解它,那么它可以很好地工作。它不会让你在数据库设计课上获得“A”,但如果它有效......
5) 实现一个愚蠢的锁表。我讨厌这个解决方案,但它确实有效——让你的线程写下它正在父“x”上工作,而其他人不能,因为它是第一个事务(和提交)。通常会导致相同的问题(以及其他问题 - 稍后清理记录等),但在子插入速度慢而单行插入速度快时可以工作。您仍然会遇到冲突,但会减少。
关于java - 使用多线程的JPA持久化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6514117/