【发布时间】: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() 方法检查事务是否处于活动状态。如果不是,它开始交易。这是这个方法的样子(跳过所有无聊的部分):
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