MySQL在相同数据集但不同线程上的荒谬行为


Absurd MySQL behavior over same data set but different threads

我从两个不同的PHP实例连接到MySQL数据库。第一个线程添加新行,通过队列将新行的"id"传递给第二个线程有时第二个线程无法找到新行数据,即使理论上它应该在第一个线程完成工作后到达数据库。

简化的伪代码看起来有点像

线程1

$db = get_mysql_connection();
$db->beginTransaction();
$rowid = $db->query("insert data..");
$db->commit();
//For Debugging purposes only
$db->check_if_row_exists($rowid); //Always returns true
send_to_queue($rowid);

线程2

$rowid = fetch_from_queue();
$db = get_mysql_connection();
$db->check_if_row_exists($rowid); //Sometimes returns false;
usleep(1000000);
$db->check_if_row_exists($rowid); //Always returns true.

我不明白,为什么线程1显示数据有一个有效的条目,而线程2(它肯定会在线程1之后的某个时候进行查询)却找不到数据。我正在使用事务提交数据,这是在做一些奇怪的事情吗?

我使用Gearman作为队列。线程1通过Apache运行,而线程2只是作为一个独立进程运行。

编辑1:当线程2与线程1同时运行时,就会发生这种情况。显然,线程1达到了某种竞赛条件,但我不知道为什么。

编辑2:正如N.B.所指出的,Innodb延迟将数据写入磁盘,因此它对第二个线程不可见。

我应该如何处理这种情况?睡眠/Usleep几乎总是一个次优的解决方案,因为在高负载条件下,磁盘i/o时间可能会增加。有没有办法"通知"第二个线程Innodb已经完成了磁盘i/o?

从我的评论中粘贴,看到没有必要更改任何内容:

这里没有比赛条件。线程1可以查看其事务和数据。线程2不能,因为它们还没有到达磁盘(否fsync调用尚未由InnoDB进行)。所以很自然,你会一直查看线程1中的数据,但如果在调用-您不会在线程2中看到它。线程1和2不共享相同的mysql连接线程,两者都使用不同的连接线程。所以简而言之,如果不在磁盘上,则没有可用的数据。它不在磁盘上因为InnoDB将延迟写入,直到驱动器准备好写入。这就是为什么你会在我们睡觉后看到它。