我正在尝试将表中的一行锁定为“正在使用”,这样当我的cron每分钟运行一次时,我不会处理两次数据。由于我的脚本运行所需的时间长度,cron将导致脚本的多个实例一次运行(通常一次约5或6)。出于某种原因,我的“使用中”方法并不总是有效。
我不想要锁定表,因为我需要它们可用于同时处理,这就是为什么我使用'inuse'字段进行伪锁定各行的路由。我不知道有更好的办法。
以下是我的困境的例证:
<?
//get the first row from table_1 that is not in use
$result = mysqli_query($connect,"SELECT * FROM `table_1` WHERE inuse='no'");
$rows = mysqli_fetch_array($result, MYSQLI_ASSOC);
$data1 = $rows[field1];
//"lock" our row by setting inuse='yes'
mysqli_query($connect,"UPDATE `table_1` SET inuse='yes' WHERE field1 = '$data1'");
//insert new row into table_2 with our data if it doesn't already exist
$result2 = mysqli_query($connect,"SELECT * FROM `table_2` WHERE field='$data2'");
$numrows = mysqli_num_rows($result2);
if($numrows >= 1) {
//do nothing
} else {
//run some unrelated script to get data
$data2 = unrelatedFunction();
//insert our data into table_2
mysqli_query($connect,"INSERT INTO `table_2` (field) value ('$data2')");
}
//"unlock" our row in table_1
mysqli_query($connect,"UPDATE `table_1` SET inuse='no' WHERE field1 = '$data1'");
?>
你会在这里看到,如果$ data2已存在一行,则不会收集和插入$ data2,但该部分用于错误检查,并且由于错误仍然存在而无法回答我的问题。我试图理解为什么(如果我没有那里的错误检查)我的'inuse'方法有时被忽略,我在table_2中得到重复的行,其中包含$ data2。
答案 0 :(得分:1)
在第一次选择和第一次更新之间有很多时间,其他进程可以执行相同的操作。您也没有使用交易,因此您无法保证其他人可以看到任何更改顺序。
您可以将所有内容移动到具有所需隔离级别的事务中,并使用SELECT .... FOR UPDATE
syntax。或者您可以尝试以不同的方式进行复制。例如,更新要处理的N行和SET in_use=your_current_pid WHERE in_use IS NULL
。然后,您可以回读手动标记为处理的行。完成后,将in_use重置为NULL
。