我有一个innoDB表,其中多个连接可能会插入数据,而每10秒运行一个MySql事件会删除一些以前插入的记录。
但我得到了死锁。我该如何避免它们?
此代码负责逐行将记录插入表中。
sql = "INSERT INTO ex_result (Result_ID, ParentResult_ID, StepNumber, Name, Type, DB_ID, Session_ID, Status, TotalTime, FunctionCall, FunctionResult, ToolTime, PluginName, Screenshot_FID, IsNegative, ContinueOnError, WantSnapshot, Message, ResultCode, Output, InputArguments) VALUES (@Result_ID, @ParentResult_ID, @StepNumber, @Name, @Type, @DB_ID, @Session_ID, @Status, @TotalTime, @FunctionCall, @FunctionResult, @ToolTime, @PluginName, @Screenshot_FID, @IsNegative, @ContinueOnError, @WantSnapshot, @Message, @ResultCode, @Output, @InputArguments)"
该活动的代码是:
DELIMITER //
CREATE EVENT EVENT_RESULT_CLEANER
ON SCHEDULE
EVERY 10 second
COMMENT 'Clean all delted results'
DO
BEGIN
DROP TEMPORARY TABLE IF EXISTS `Temp_Result_Purge`;
CREATE TEMPORARY TABLE `Temp_Result_Purge` (
`Result_ID` VARCHAR(63) NOT NULL,
PRIMARY KEY (`Result_ID`))
ENGINE = MEMORY;
INSERT INTO Temp_Result_Purge(Result_ID)
(
SELECT t1.result_id
FROM ex_result AS t1
INNER JOIN ex_session as t2
ON t1.session_id=t2.session_id
WHERE NOT EXISTS(SELECT t3.result_id FROM ex_result as t3 WHERE t3.parentresult_id=t1.result_id)
AND t2.DeletedOn IS NOT NULL
LIMIT 2000
);
DELETE t1 FROM `ex_result` AS t1 INNER JOIN
`Temp_Result_Purge` AS t2 ON t1.Result_ID=t2.Result_ID;
DROP TEMPORARY TABLE `Temp_Result_Purge`;
END//
DELIMITER ;
答案 0 :(得分:1)
至少,您需要在事件代码中启动事务。
这不是由开始...结束,请参阅http://dev.mysql.com/doc/refman/5.6/en/commit.html
在所有存储的程序中(存储过程和函数, 触发器和事件),解析器将BEGIN [WORK]视为 BEGIN ... END块的开头。在此开始交易 而不是START TRANSACTION的上下文。
此外,如果您每隔10秒运行一次,请考虑更改您的体系结构。对于寿命较短的数据,关系数据库不太好。另一种选择可能是消息队列。
答案 1 :(得分:1)
首先,我有一些令人讨厌的事情要说,然后我会找到一个可能的解决方案。
“不要排队,只是这样做。” - MySQL没有成为一个好的排队引擎。
添加BEGIN ... COMMIT(如前所述)。 BEGIN ... COMMIT也需要围绕其他代码。
添加代码以测试死锁。然后重播BEGIN ... COMMIT。你无法避免所有死锁,所以请为它们做好计划。
仅将LIMIT降低到10,然后将清除器放入连续循环中,而不是每10秒钟唤醒一次。 (如果您要说“会让事情变得更糟”,那么请继续阅读;我会给您一个可能更好的变体。)
使用LEFT JOIN ... WHERE ... IS NULL
代替NOT EXISTS ( SELECT ... )
不要一遍又一遍地重新制作表格;只是TRUNCATE TABLE
。或者,更好的是,只需直接删除而无需通过tmp表。然而,这导致另一个问题......
查询必须经过多少行才能找到LIMIT行?请记住,SELECT正在干扰INSERT。如果它通常需要扫描一百万行才能找到要删除的2000,那么我们需要找到一种方法来使行更容易找到。为此,我们需要有关您的应用和表格大小的更多详细信息。或者思考一下......
礼貌地扫描一百万行以找到一些行的一种技术是一次遍历表1000行,通常使用PRIMARY KEY。注意:这是表的1000行,而不是1000行有资格删除。在每1000个之后,删除你找到的那些(如果有的话),然后继续下一个1000.当你到达表的末尾时,重新开始。有关如何执行此分块的详细信息,请访问:http://mysql.rjweb.org/doc.php/deletebig#deleting_in_chunks
答案 2 :(得分:0)
在我看来,最好的解决方案是使用soft deletes
只需将已删除对象的状态设置为已删除即可。如果记录数量非常巨大,并且您根本不想存储历史数据,则可以每晚或每周进行一次调度数据库清除
缺点之一是您必须通过添加一个新条件来重写数据检索逻辑