php - MySQL 和 PHP : Atomicity and re-entrancy of a PHP code block executing two subsequent queries - how dangerous?

标签 php mysql atomic reentrancy

在 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. 实例1尝试选择记录,没有找到记录,进入插入查询 block
  2. 实例2尝试选择记录,没有找到记录,进入插入查询 block
  3. 实例 1 尝试插入记录,成功
  4. 实例 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 并锁定整个表,我不想这样做,因为与更新/插入相比,我不想这样做选择。

最佳答案

看来您已经知道问题的答案以及如何解决您的问题。这是一个真实的问题,您可以使用以下方法之一来解决它:

  • 选择...进行更新
  • 插入...重复 key 更新
  • 交易(不要使用 MyIsam)
  • 表锁

关于php - MySQL 和 PHP : Atomicity and re-entrancy of a PHP code block executing two subsequent queries - how dangerous?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3104365/

相关文章:

PHP 和 MySQL 数据插入错误

php - nginx 从 https 排除路径

c - Linux 上 C 语言的原子操作

django - 如何在Django中输入完整的历史记录?

mysql - 我可以自动重命名/替换 2 个或更多表和 View 吗?

php - MySQL 函数声明。不适用于 PHP。从 phpmyadmin 运行没问题。不创建函数

php - 预期 user_string 是一个字符串,在第 1662 行的 C :\xampp. .. 中给出的空值

mysql - Golang Gorm : Same query constructed differently throwing different results

mysql - 获取上周工作日的记录

mysql - 如何在mysql case函数中执行子查询?