【发布时间】:2013-08-09 16:36:47
【问题描述】:
外键可能是解决此问题的最佳方法。但是,我正在尝试了解表锁定/事务,因此我希望我们暂时可以忽略它们。
假设我在 InnoDB 数据库中有两个表:categories 和 jokes;并且我正在使用 PHP/MySQLi 来完成这项工作。表格如下所示:
CATEGORIES
id (int, primary, auto_inc) | category_name (varchar[64])
============================================================
1 knock, knock
JOKES
id (int, primary, auto_inc) | category_id (int) | joke_text (varchar[255])
=============================================================================
empty
这里有两个函数,每个函数都被不同的连接同时调用。电话是:delete_category(1) 和 add_joke(1,"Interrupting cow. Interrup-MOOOOOOOO!")
function delete_category($category_id) {
// only delete the category if there are no jokes in it
$query = "SELECT id FROM jokes WHERE category_id = '$category_id'";
$result = $conn->query($query);
if ( !$result->num_rows ) {
$query = "DELETE FROM categories WHERE id = '$category_id'";
$result = $conn->query($query);
if ( $conn->affected_rows ) {
return true;
}
}
return false;
}
function add_joke($category_id,$joke_text) {
$new_id = -1;
// only add the joke if the category exists
$query = "SELECT id FROM categories WHERE id = '$category_id'";
$result = $conn->query($query);
if ( $result->num_rows ) {
$query = "INSERT INTO jokes (joke_text) VALUES ('$joke_text')";
$result = $conn->query($query);
if ( $conn->affected_rows ) {
$new_id = $conn->insert_id;
return $new_id;
}
}
return $new_id;
}
现在,如果两个函数的 SELECT 语句同时执行,并从那里继续,delete_category 会认为可以删除类别,add_joke 会认为可以添加笑话现有的类别,所以我会得到一个空的 categories 表和 joke 表中引用不存在的 category_id 的条目。
如果不使用外键,你将如何解决这个问题?
到目前为止,我最好的想法是执行以下操作:
1) "LOCK TABLES categories WRITE, jokes WRITE" 在delete_category 的开头。但是,由于我使用的是 InnoDB,因此我非常希望避免锁定整个表(尤其是经常使用的主要表)。
2) 使add_joke 成为一个事务,然后在插入记录之后执行"SELECT id FROM categories WHERE id = '$category_id'" 。如果此时不存在,则回滚事务。但是,由于add_joke 中的两个SELECT 语句可能返回不同的结果,我相信我需要研究我不熟悉的事务隔离级别。
在我看来,如果我做了这两件事,它应该会按预期工作。尽管如此,我还是很想听到更明智的意见。谢谢。
【问题讨论】:
-
所以您正试图将笑话添加到当前为空的类别中?而且由于您还删除了空类别,因此首先将其删除?尝试学习锁定是完全假设的,还是真的发生了?
-
没错。如果代码按特定顺序执行,则可以删除类别(因为没有笑话)但同时可以添加笑话(因为类别仍然存在)。这与我编写的一些旧代码有关,因此可能会发生,我正在尝试使用它来了解锁定/事务。
-
好吧,这就是我感到困惑的原因。只要确保 add_joke() 总是在 delete_category() 之前执行,那么所有这些都可以避免。
-
每个函数都是从不同的连接调用的。如果您对如何避免该问题有任何想法,请发布答案。
标签: php mysql transactions innodb table-locking