在 MySQL 中,我必须检查 select 查询是否返回了任何记录,如果没有,我插入一条记录。我担心 PHP 脚本中的整个 if-else 操作并不像我希望的那样原子,即在某些情况下会中断,例如,如果在需要处理相同记录的情况下调用脚本的另一个实例:
if(select returns at least one record)
{
update record;
}
else
{
insert record;
}
我在这里没有使用事务,并且自动提交是打开的。我正在使用 MySQL 5.1 和 PHP 5.3。该表是 InnoDB。我想知道上面的代码是否次优并且确实会中断。我的意思是相同的脚本被两个实例重新输入,并且发生以下查询序列:
- 实例1尝试选择记录,没有找到,进入block进行insert查询
- 实例2尝试选择记录,没有找到,进入block进行insert查询
- 实例 1 尝试插入记录,成功
- 实例 2 尝试插入记录,失败,自动中止脚本
这意味着实例 2 将中止并返回错误,跳过插入查询语句之后的任何内容。我可以使错误不是致命的,但我不喜欢忽略错误,我更愿意知道我的恐惧是否在这里真实存在。
更新:我最终做了什么(这样可以吗?)
有问题的表有助于限制(实际上是允许/拒绝)应用程序发送给每个收件人的消息数量。系统不应在 Z 期间内向收件人 Y 发送超过 X 条消息。该表 [概念上] 如下:
create table throttle
(
recipient_id integer unsigned unique not null,
send_count integer unsigned not null default 1,
period_ts timestamp default current_timestamp,
primary key (recipient_id)
) engine=InnoDB;
还有 [有点简化/概念性的] PHP 代码块,它应该执行原子事务,维护表中的正确数据,并根据油门状态允许/拒绝发送消息:
function send_message_throttled($recipient_id) /// The 'Y' variable
{
query('begin');
query("select send_count, unix_timestamp(period_ts) from throttle where recipient_id = $recipient_id for update");
$r = query_result_row();
if($r)
{
if(time() >= $r[1] + 60 * 60 * 24) /// The numeric offset is the length of the period, the 'Z' variable
{/// new period
query("update throttle set send_count = 1, period_ts = current_timestamp where recipient_id = $recipient_id");
}
else
{
if($r[0] < 5) /// Amount of messages allowed per period, the 'X' variable
{
query("update throttle set send_count = send_count + 1 where recipient_id = $recipient_id");
}
else
{
trigger_error('Will not send message, throttled down.', E_USER_WARNING);
query('rollback');
return 1;
}
}
}
else
{
query("insert into throttle(recipient_id) values($recipient_id)");
}
if(failed(send_message($recipient_id)))
{
query('rollback');
return 2;
}
query('commit');
}
好吧,不管发生 InnoDB 死锁的事实,这很好,不是吗?我没有捶胸顿足之类的,但这只是我能做到的性能/稳定性的最佳组合,没有使用 MyISAM 并锁定整个表,我不想这样做,因为更频繁的更新/插入 vs选择。