Mysql SQL表锁定竞争条件-选择然后插入
MariaDB/MySQL有一个很好的锁定问题 服务器正在重新组装多部分SMS消息。消息以分段的方式到达。具有相同“smsfrom”和“uniqueid”的段是同一消息的一部分。段的段号从1开始到“segmenttotal”。当消息的所有部分都到达时,消息就完成了。我们有一个待重新组装的不匹配段表,如下所示:Mysql SQL表锁定竞争条件-选择然后插入,mysql,sql,Mysql,Sql,MariaDB/MySQL有一个很好的锁定问题 服务器正在重新组装多部分SMS消息。消息以分段的方式到达。具有相同“smsfrom”和“uniqueid”的段是同一消息的一部分。段的段号从1开始到“segmenttotal”。当消息的所有部分都到达时,消息就完成了。我们有一个待重新组装的不匹配段表,如下所示: CREATE TABLE frags ( smsfrom TEXT, uniqueid VARCHAR(32) NOT NULL, sms
CREATE TABLE frags (
smsfrom TEXT,
uniqueid VARCHAR(32) NOT NULL,
smsbody TEXT,
segmentnum INTEGER NOT NULL,
segmenttotal INTEGER NOT NULL);
当一个新的细分市场进入时,我们在交易中
SELECT ... FROM frags WHERE smsfrom = % AND uniqueid = %;
这让我们了解到目前为止收到的所有片段。如果新的
加上这些有所有的段号,我们有一个完整的信息。
我们发送消息进行进一步处理,并删除相关片段。好的
如果不是所有的片段都已到达,我们将插入刚得到的片段。“自动提交”处于禁用状态,因此这两个操作都是事务的一部分。顺便提一下,InnoDB引擎
这有一个竞赛条件。对于两段消息,两段同时进入,并由单独的进程处理。进程A进行选择,但未找到任何内容。进程B进行选择,但未找到任何内容。进程A插入段1,没有问题。过程B插入段2,没有问题。现在我们被卡住了——所有的部分都在表中,但我们没有注意到。因此,信息永远停留在那里。(实际上,我们每隔几分钟进行一次清理,以删除旧的不匹配的内容,但现在忽略此操作。)
那怎么了?选择“不锁定行”,因为它们找不到任何内容。
我们需要一个尚未存在的行上的行锁。将更新添加到选择中没有帮助;没什么要锁的。也不会锁定在共享模式下。即使使用可序列化的事务类型也无济于事,因为这只是共享模式下的全局锁
好的,假设我们先进行插入,然后进行选择,看看是否有所有的线段。进程A插入1,没有问题。进程B插入2,没有问题。进程A进行选择,只看到1。进程B进行选择,只看到2个。这就是可重复阅读语义。不好
蛮力方法在执行任何操作之前都是一个锁表。这应该是可行的,尽管这很烦人,因为我正在处理一个涉及其他表的事务,而锁表意味着提交
在每次插入后进行提交可能有效,但我不能完全确定
有更优雅的解决方案吗?为什么没有
1) 过程1。插入到您的frag表中。没有别的了
插入。。。。
承诺
2) 过程2
这将通过查找完整的多部分SMS
按smsfrom、unique、unique having count()=段总数从frags组中选择smsfrom、uniqueid、uniqueid、count()
将它们移到新表中
从碎片中删除,其中smsfrom=和unique=
承诺 正如我在上面所写的,我最终做了以下工作:
INSERT ... -- Insert new fragment.
COMMIT
SELECT ... FROM frags WHERE smsfrom = % AND uniqueid = % FOR UPDATE;
检查SELECT是否返回了完整的片段集。如果是,重新组装并处理消息,然后
DELETE ... FROM FRAGS WHERE smsfrom = % AND uniqueid = %;
提交和更新都是必需的。需要提交,以便每个进程都能看到来自另一个进程的任何插入。在SELECT行上需要FOR UPDATE来锁定所有片段,直到完成删除。否则,两个进程可能会在SELECT和Remoble中看到完整的片段集,并对消息进行两次处理
这对于一个单表问题来说非常复杂,但似乎有效 如果您在提交后将其他段的检查移动到?需要一些方法来确保进程A和进程B不同时处理消息。我最后做了类似的事情—插入、提交,然后在一个进程中选择。必须对另一个select执行select FOR UPDATE以获得行锁,否则两个进程都可以找到组装好的消息,并且它将被处理两次。很高兴听到这有助于。。。。我不会问您如何处理在成功重新组装原件后到达的OOB(带外)多部分消息:)-您可能需要对它们进行说明,或者简单地删除超过2天的任何内容-这取决于您的业务需求。