Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/mysql/59.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php InnoDB仅在引用id存在时插入记录(无外键)_Php_Mysql_Transactions_Innodb_Table Locking - Fatal编程技术网

Php InnoDB仅在引用id存在时插入记录(无外键)

Php InnoDB仅在引用id存在时插入记录(无外键),php,mysql,transactions,innodb,table-locking,Php,Mysql,Transactions,Innodb,Table Locking,外键可能是解决此问题的最佳方法。但是,我正在尝试了解表锁定/事务,因此我希望我们暂时可以忽略它们。 让我们假设我在InnoDB数据库中有两个表:类别和笑话;我正在使用PHP/MySQLi来完成这项工作。这些表看起来是这样的: CATEGORIES id (int, primary, auto_inc) | category_name (varchar[64]) ============================================================ 1

外键可能是解决此问题的最佳方法。但是,我正在尝试了解表锁定/事务,因此我希望我们暂时可以忽略它们。

让我们假设我在InnoDB数据库中有两个表:
类别
笑话
;我正在使用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\u category(1)
add\u笑话(1,“interrupt cow.Interrup mooooooo!”)

现在,如果两个函数中的
SELECT
语句同时执行,然后继续执行,
delete\u category
将认为可以删除该类别,
add\u joke
将认为可以将该笑话添加到现有类别中,因此,我将得到一个空的
categories
表和
joke
表中的一个条目,该条目引用了一个不存在的
category\u id

如果不使用外键,您将如何解决此问题?

到目前为止,我最好的想法是做以下几点:

1)
“锁定表格类别写入,笑话写入”
删除类别的开头。然而,由于我使用的是InnoDB,我非常希望避免锁定整个表(尤其是经常使用的主表)

2) 在插入记录后,进行
add_joke
事务处理,然后执行
“从类别中选择id,其中id=“$category_id”“”
。如果此时不存在,则回滚事务。但是,由于
add\u
中的两个
SELECT
语句可能会返回不同的结果,因此我认为我需要研究事务隔离级别,这一点我并不熟悉


在我看来,如果我同时做了这两件事,它应该会像预期的那样工作。尽管如此,我还是渴望听到更多有见地的意见。谢谢。

只有在没有匹配笑话的情况下,才能删除类别:

DELETE c FROM categories AS c
LEFT OUTER JOIN jokes AS j ON c.id=j.category_id
WHERE c.id = $category_id AND j.category_id IS NULL;
如果类别有任何笑话,联接将找到它们,因此外部联接将返回非空结果。WHERE子句中的条件消除了非空结果,因此整个删除将匹配零行

同样,仅当类别存在时,才能将笑话插入类别:

INSERT INTO jokes (category_id, joke_text)
SELECT c.id, '$joke_text'
FROM categories AS c WHERE c.id = $category_id;
如果没有此类类别,则SELECT返回零行,INSERT为no-op

这两种情况都会在categories表上创建一个共享锁(S锁)

S锁的演示:

在一个会话中,我运行:

mysql> INSERT INTO bar (i) SELECT SLEEP(600) FROM foo;
mysql> SHOW ENGINE INNODB STATUS\G
. . .
---TRANSACTION 3849, ACTIVE 1 sec
mysql tables in use 2, locked 2
2 lock struct(s), heap size 376, 1 row lock(s)
MySQL thread id 18, OS thread handle 0x7faefe7d1700, query id 203 192.168.56.1 root User sleep
insert into bar (i) select sleep(600) from foo
TABLE LOCK table `test`.`foo` trx id 3849 lock mode IS
RECORD LOCKS space id 22 page no 3 n bits 72 index `GEN_CLUST_INDEX` of table `test`.`foo` trx id 3849 lock mode S
在第二节课中,我运行:

mysql> INSERT INTO bar (i) SELECT SLEEP(600) FROM foo;
mysql> SHOW ENGINE INNODB STATUS\G
. . .
---TRANSACTION 3849, ACTIVE 1 sec
mysql tables in use 2, locked 2
2 lock struct(s), heap size 376, 1 row lock(s)
MySQL thread id 18, OS thread handle 0x7faefe7d1700, query id 203 192.168.56.1 root User sleep
insert into bar (i) select sleep(600) from foo
TABLE LOCK table `test`.`foo` trx id 3849 lock mode IS
RECORD LOCKS space id 22 page no 3 n bits 72 index `GEN_CLUST_INDEX` of table `test`.`foo` trx id 3849 lock mode S
您可以看到,这在表foo上创建了一个IS锁,在我正在读取的表foo的一行上创建了一个S锁

对于任何混合读/写操作,如
SELECT…for UPDATE
INSERT…SELECT
CREATE TABLE…SELECT
,都会发生同样的情况,以阻止正在读取的行在作为写操作的源时被修改


IS锁是一个表级锁,用于防止对表进行DDL操作,因此,当此事务取决于表中的某些内容时,没有人发出
DROP table
ALTER table

那么,您是在尝试向当前为空的类别添加笑话吗?既然你也在删除空类别,那你先删除它吗?这完全是为了学习锁定,还是真的发生了?没错。如果代码按特定顺序执行,则可以删除类别(因为没有笑话),但同时可以添加笑话(因为类别仍然存在)。这与我编写的一些旧代码有关,所以它可能会发生,我正试图用它来学习锁定/事务。好吧,这就是我感到困惑的原因。只要确保在删除类别()之前始终执行add_joke(),所有这些都可以避免。每个函数都是从不同的连接调用的。如果你有一个如何避免这个问题的想法,请发布一个答案。绝对精彩的答案,非常感谢-正是我想要的。