相同数据集但不同线程的Absurd MySQL行为

时间:2012-03-14 15:50:39

标签: php mysql mysqli

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

简化的伪代码类似于

线程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延迟将数据写入磁盘,因此它对第二个线程不可见。

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

1 个答案:

答案 0 :(得分:0)

从我的评论中发现,没有必要改变任何东西:

  

这里没有竞争条件。线程1可以看到它的交易   和数据。线程2不能,因为它们还没有到达磁盘(没有   InnoDB已经进行了fsync调用。很自然,你永远都是   查看来自线程1的数据,但是如果它在当时不在磁盘上   调用 - 你不会在线程2中看到它。线程1和2不会   共享SAME mysql连接线程,两者都使用不同的。所以   缩短它 - 如果不是在磁盘上,没有数据可用。它不在磁盘上   因为InnoDB会延迟写入,直到驱动器准备好写入。   这就是为什么你在睡觉后看到它。