23

一个两部分的问题:

  1. 在我的 CodeIgniter 脚本中,我开始一个事务,然后插入一行,将 insert_id() 设置为一个 php 变量,使用新 ID 作为外键将更多行插入另一个表,然后我提交所有内容。

    所以我的问题是:如果在结束事务之前一切都没有提交,如果没有插入任何东西,mysql如何能够返回最后一个插入 ID?我的脚本(几乎)完美地工作,在后续查询中使用了新 ID。

    (我说“几乎”是因为,使用 PDO mysql 驱动程序,有时应该返回 insert_id() 的第一个插入是重复的——它被插入了两次。知道为什么会这样吗?这与获取最后一个有关吗ID?如果使用 mysqli 或 mysql 驱动程序,它永远不会发生。)

  2. 我首先编写了没有事务的脚本,所以我有一些代码可以一路检查 mysql 错误,例如:

    if(!$this->db->insert($table, $data)) {
        //log message here
    }
    

    一旦我将所有 mysql 代码包装在事务中,这将如何影响 mysql 进程?它不会导致任何可见的错误(希望与上述问题无关),但应该将其删除吗?

谢谢你。

4

1 回答 1

32

要回答你的第一个问题...

使用事务时,就您的连接而言,您的查询会正常执行。您可以选择提交、保存这些更改,或者回滚、还原所有更改。考虑以下伪代码:

insert into number(Random_number) values (rand()); 
select Random_number from number where Number_id=Last_insert_id();

//php

if($num < 1)
   $this->db->query('rollback;'); // This number is too depressing.
else
   $this->db->query('commit;'); // This number is just right.

生成的随机数可以在提交之前读取,以确保它在保存之前适合所有人查看(例如,提交和解锁行)。

如果 PDO 驱动程序不起作用,请考虑使用 mysqli 驱动程序。如果这不是一个选项,您始终可以使用查询“select last_insert_id() as id;” 而不是 $this->db->insert_id() 函数。

要回答您的第二个问题,如果您要插入或更新其他模型将要更新或读取的数据,请务必使用事务。例如,如果将“Number_remaining”列设置为 1,则可能会出现以下问题。

Person A reads 1
Person B reads 1
Person A wins $1000!
Person A updates 1 to be 0
Person B wins $1000!
Person B updates 0 to be 0

在相同情况下使用事务将产生以下结果:

人 A 开始事务
人 A 从 Number_remaining 读取“1” (如果使用select for update
,则 该行现在被锁定)人 B 尝试读取 Number_remaining - 被迫等待人 A 赢得 $1000人 A 将 1 更新为 0人 A 提交人B 读到 0人 B 没有赢得 $1000人 B 哭







您可能还想阅读事务隔离级别

小心死锁,在这种情况下可能会发生:

A 读取第 1 行 ( select ... for update)
B 读取第 2 行 ( select ... for update)
A 尝试读取第 2 行,被迫等待
B 尝试读取第 1 行,被迫等待
A 达到innodb_lock_wait_timeout(默认 50 秒)并断开连接
B 读取第 1 行并正常继续

最后,由于 Person B 可能已经到达 PHP 的max_execution_time,当前查询将独立于 PHP 完成执行,但不会收到进一步的查询。如果这是一个 autocommit=0 的事务,当与您的 PHP 服务器的连接被切断时,查询将自动回滚。

于 2013-03-14T05:21:42.170 回答