我尝试使用 多个线程 持久化对象时遇到问题。
细节 :
假设我有一个对象PaymentOrder,该对象具有PaymentGroup(一对多关系)PaymentGroup列表,并且又包含(一对多关系)列表CreditTransfer。
PaymentOrder
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。
PAYMENT_ORDER_MASTER
PAYMENT_GROUPS
CREDIT_TRANSFERS
我如何解决这个问题,同时维护拆分主付款顺序使用组逻辑和多个线程?
你有选择。 1)原始但简单,最后捕获密钥冲突错误,然后在没有父母的情况下重试插入。假设您的父母确实是独一无二的,您就会知道父母刚刚采取了另一种措施……带着孩子。与其他选项相比,这可能效果不佳,但也许您会得到所需的流行音乐。如果您的父母中有一个孩子的父母百分比很高,则效果很好。
2)更改您的读取一致性级别。它是特定于供应商的,但是您有时可以读取未提交的事务。这将有助于您在提交之前查看其他线程的工作。它不是万无一失的,您仍然必须执行#1,因为读取后另一个线程可能会潜入。但这可能会提高吞吐量,但会增加复杂性。基于RDBMS可能是不可能的(或者可能会发生,但只能在数据库级别上,弄乱其他应用程序!)
3)用单线程使用者实现工作队列。如果程序的主要昂贵工作在持久性级别之前,则可以让线程将其数据“插入”到工作队列中,在其中不执行键。然后从工作队列中拉出一个线程并继续执行。工作队列可以在内存中,在另一个表中或在供应商特定的位置(Weblogic Queue,Oracle AQ等)。如果程序的主要工作在持久性之前,则对THAT进行并行处理,然后返回到插入上的单个线程。您甚至可以使消费者在“批量插入”模式下工作。Sweeeeeeeet。
4)放松约束。谁真正在乎同一个孩子是否有两个父母持有相同的信息?我只是问问。如果以后您不需要父信息的超快速更新,并且可以更改阅读程序以了解它,那么它可以很好地工作。在数据库设计课程中,它不会使您获得“ A”,但如果可以的话…
5)实现一个愚蠢的锁表。我讨厌这个解决方案,但是它确实有效—让您的线程写下它正在父级“ x”上工作,并且没有其他人可以作为它的第一个事务(和提交)。通常会导致相同的问题(以及其他问题–稍后清除记录等),但是在子插入速度较慢而单行插入速度较快时也可以使用。您仍然会有碰撞,但是碰撞更少。