在网站上,当用户发表评论时,我会进行几个查询,即插入和更新。 (在MariaDB 10.1.29上)
我使用BEGIN TRANSACTION
,因此,如果任何查询在任何给定时间失败,我都可以轻松地回滚并删除所有更改。
现在,我注意到这锁定了某些表或行,并且在查询运行时我没有说话,这很明显,但是直到事务未关闭。
好处是它不会阻止UPDATE
,并且DELETE
仅在它们共享一个公用索引键(同一页的注释)时才被锁定。
但是随后,INSERT
锁定了任何INSERT
中的表。
我可以执行任何不会从新插入中锁定表的事务(在事务正在进行时,而不是实际查询),或任何其他方法可以让我方便地“撤消”在某点之后完成的任何查询吗?
PD:
[Table] => comment
[Create Table] => CREATE TABLE `comment` (
`com_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`com_p_id` int(10) unsigned NOT NULL,
`com_cont` varchar(282) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
`com_user` varchar(22) COLLATE utf8_bin NOT NULL,
`com_us_id` int(10) unsigned NOT NULL,
`reply_id` int(10) unsigned DEFAULT NULL,
`replies` mediumint(8) unsigned NOT NULL DEFAULT '0',
`noti_reply` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`com_id`) USING BTREE,
KEY `coments_ibfk_1` (`com_p_id`),
KEY `reply_id` (`reply_id`)
) ENGINE=InnoDB AUTO_INCREMENT=11108 DEFAULT CHARSET=utf8 COLLATE=utf8_bin
我从不带任何mysqli_begin_transaction()
的PHP函数flags
开始事务,然后开始mysqli_commit()
。
这里是查询,只有在得到答复的情况下,首先进行UPDATE
。
UPDATE comment SET replies=replies+1 WHERE com_id = ?;
INSERT INTO comment (com_p_id,com_cont,com_user,com_us_id,reply_id,replies,noti_reply) VALUES (?,?,?,?,?,0,?);
UPDATE posts SET post_comm=post_comm+1, trend = @addtrend := trend+2, post_b_id = @thisblog := post_b_id WHERE post_id = ?;
保存@variables是为了在提交后做的不太重要的事情。
答案 0 :(得分:1)
我认为,简单的INSERT不会阻止其他插入的时间超过插入时间。 AUTO_INC locks不会在整个交易时间内被保留。
但是,如果两个事务尝试像下面的语句中那样更新同一行(两次回复同一条评论)
UPDATE comment SET replies=replies+1 WHERE com_id = ?
第二个必须等待直到第一个被提交。您需要该锁以使计数(回复)保持一致。
我认为您所能做的就是保持交易时间尽可能短。例如,您可以在开始事务之前准备所有语句。但这只是几毫秒。如果传输文件可能需要40秒钟,那么在数据库事务打开时不应该这样做。在开始事务之前,请先传输文件,然后使用表示操作未完成的名称进行保存。您也可以将它们保存在不同的文件夹中,但是在同一分区上。然后,在运行事务时,您只需要重命名文件,这不需要花费很多时间。您可以不定期清理并删除未重命名的文件。
答案 1 :(得分:1)
所有写操作都以类似的方式工作-它们锁定执行语句(直到可能通过COMMIT
关闭事务之前接触(或可能接触)的行或ROLLBACK
。 SELECT...FOR UPDATE
和SELECT...WITH SHARED LOCK
也参与其中。
发生写操作时,将完成死锁检查。
在某些情况下,存在“间隙”锁定。 com_id
恰好是表格中的最后一个ID吗?
您是否遗漏了需要SELECTs
的任何FOR UPDATE
?