【问题标题】:Data is still visible after deletion within the same MySQL transaction在同一个 MySQL 事务中删除后数据仍然可见
【发布时间】:2014-09-26 23:40:27
【问题描述】:

最近我一直在测试我的 PHP 框架的数据库包装类,它基于 PHP 数据对象。当我遇到一个看起来像是 ACID 噩梦的错误时,我已经成功通过了 Oracle 数据库的所有测试并开始使用 MySQL 进行测试。

简而言之,我的数据库驱动程序包装类执行以下操作:

1) 它建立一个具有以下属性的持久数据库连接

自我::$connection = 新 PDO( $dsn ,DATABASE_USERNAME ,DATABASE_PASSWORD ,[ PDO::ATTR_AUTOCOMMIT => FALSE // 不要自动提交每一条语句 ,PDO::ATTR_CASE => PDO::CASE_LOWER // 强制列名小写 ,PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC // 返回结果集为按列名索引的数组 ,PDO::ATTR_EMULATE_PREPARES => (DATABASE_DRIVER == 'mysql') // 只允许模拟 MySQL 准备好的语句 ,PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION // 抛出异常并在错误时回滚事务 ,PDO::ATTR_ORACLE_NULLS => PDO::NULL_EMPTY_STRING // 将 emtpy 字符串转换为 NULL ,PDO::ATTR_PERSISTENT => TRUE // 使用持久连接 ] );

2) Wrapper 类有一个execute() 方法,它是运行各种SQL 语句的主干。当使用execute() 方法执行SQL 语句时,它使用PDO::inTransaction() 方法检查事务是否处于活动状态。如果不是,它开始交易。这是这个方法的样子(跳过所有无聊的部分):

公共静态函数执行($sql,$bind_values = [],$limit = -1,$offset = 0){ ... if (!self::$connection->inTransaction()) { self::$connection->beginTransaction(); } ... }

3) 到目前为止一切顺利。但是让我们看看下面的例子,它调用了 DELETE 语句,然后是一个 SELECT 语句,对同一个表具有非常相同的 where 条件:

数据库::执行('从 dms_test 中删除 id = 5'); $data = database::execute('SELECT FROM dms_test WHERE id = 5');

4) 每个人都希望 SELECT 语句返回一个空的结果集,因为之前的 DELETE 语句只是清除了同一事务中的所有数据。

5) 尽管听起来很疯狂,但 SELECT 语句返回非空结果集,就好像永远不会发出 DELETE 语句一样。

6) 有趣的是,同样的示例在 Oracle 数据库中也能按预期工作。

知道 MySQL 有什么问题吗? 你们中有人遇到过类似的问题吗?

【问题讨论】:

  • PDO::beginTransaction 关闭自动提交模式。当自动提交模式关闭时,通过 PDO 对象实例对数据库所做的更改不会提交,直到您通过调用 PDO::commit() 结束事务。调用 PDO::rollBack() 将回滚对数据库的所有更改并将连接返回到自动提交模式。在此处发布之前请阅读手册。
  • 您的dms_test 表使用哪个 MySQL 存储引擎? MyISAM 存储引擎不支持事务。这可以解释你的问题。
  • “每个理智的人都会期望”变得非常个人化。这只是我们在这里谈论的免费软件(像小猫一样免费,像啤酒一样免费)。尊重,轻点!
  • Daan,恕我直言,在要求我阅读手册之前,请先阅读我的帖子。自动提交模式从未启用,它通过连接属性关闭(参见第 1 点)。其次,您还没有读过我所说的,Oracle 上的相同代码可以正常工作(参见第 6 点)。第三个问题是 MySQL RDBMS 返回结果集(它不应该在同一个事务中,注意 DELETE 在 SELECT 之前)。在此示例中也没有发出 COMMIT 和 ROLLBACK。 Ollie Jones,不,我当然没有使用 MyISAM,因为它缺乏基本的 ACID 合规性。
  • 我在您的代码中看不到任何特别错误的地方,我也无法想象会发生这种情况的场景。我可以建议您记录查询以丢弃明显的问题吗?

标签: php mysql pdo transactions acid


【解决方案1】:

我能够通过完全删除 PDO 的事务机制并用我自己的替换它来解决问题。显然,在 MySQL RDBMS 和禁用自动提交模式的情况下使用 PDO 事务机制可能会导致不可预知的行为。我不知道这是 PDO 还是 MySQL 的错误。

如果您想使用 PDO 实现对数据库的开箱即用事务访问,请不要使用 PDO 的内置 PDO::beginTransaction()PDO::commit()PDO::rollback() 方法。

我建议您使用以下方法:

  1. 建立连接时,使用属性PDO::ATTR_AUTOCOMMIT => FALSE

  2. 通过声明自己的变量来跟踪事务中的状态 为此目的(例如$in_transaction

  3. 注册关闭函数调用本机数据库ROLLBACK at 请求结束(如果处于事务状态)

使用这种方法,我能够克服上述错误。

【讨论】:

    【解决方案2】:

    自动提交设置为 false,因此不会保存任何更改,除非您通过事务、提交或回滚来提交它们 PDO::ATTR_AUTOCOMMIT => 假

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-06-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-17
      相关资源
      最近更新 更多