vce*_*ick 2 mysql stored-procedures
我有以下存储过程我想提高性能
DELIMITER $$
CREATE PROCEDURE NEXT_UNIT_OF_WORK
(
IN batchId INT,
IN size INT
)
BEGIN
start transaction;
CREATE TEMPORARY TABLE IF NOT EXISTS BATCH_ITEMS_UOW ENGINE=MEMORY AS (SELECT ID FROM BATCH_ITEMS WHERE STATUS = 'UNPROCESSED' AND BATCH_JOB_ID = batchId LIMIT size);
UPDATE BATCH_ITEMS SET STATUS = 'PROCESSING' WHERE BATCH_JOB_ID = batchID AND ID IN (SELECT ID FROM BATCH_ITEMS_UOW);
SELECT * FROM BATCH_ITEMS_UOW;
DROP TABLE BATCH_ITEMS_UOW;
commit;
END $$
DELIMITER ;
Run Code Online (Sandbox Code Playgroud)
其目的是通过由批处理作业 ID 标识并限制为多个项目(大小)的存储过程获取一批批处理项目。
该查询最初似乎需要 1 秒,然后每次调用存储过程都会连续花费更长的时间。
鉴于此存储过程每分钟将被多个线程调用数百次,花费这么长时间是不可接受的。
有没有更好的方法来处理这个存储过程?
为了回答评论中出现的问题,MySQL 中的临时表归创建它们的客户端连接(会话、线程)所有,并且仅可用于创建它们的客户端连接(会话、线程),因此不存在隔离问题,但是您的连接存在潜在问题之前在那里留下了具有该名称的东西,要么是陈旧的数据,要么是具有不同结构的类似名称的表,其中任何一个都不太理想。
CREATE TEMPORARY TABLE IF NOT EXISTS
如果没有同名的表,该语句将创建临时表,但如果表已经存在,则该语句是一个非阻塞无操作,如前所述,这仍然很糟糕。更好的选择可能是在创建它之前删除位于 proc 顶部的临时表,如果它在那里,那么你总是从一个干净的环境开始。如果临时表不存在,这同样是一个空操作:
DROP TEMPORARY TABLE IF EXISTS BATCH_ITEMS_UOW;
Run Code Online (Sandbox Code Playgroud)
重要提示:如果您在 BATCH_ITEMS 表的 STATUS 列上没有索引,这将是此过程比所需速度慢的两个主要原因之一。无论是使用此代码还是现有代码,这对于性能来说都是必不可少的。
最好为临时表建立索引,以使优化器的工作尽可能轻松。请注意,声明的列必须与 中列的名称或别名相同SELECT
,否则 MySQL 假定SELECT
应该使用 中的列名为临时表生成附加列SELECT
:
CREATE TEMPORARY TABLE BATCH_ITEMS_UOW (
ID INT NOT NULL PRIMARY KEY
) ENGINE=MEMORY AS (
SELECT ID FROM BATCH_ITEMS
WHERE STATUS = 'UNPROCESSED'
AND BATCH_JOB_ID = batchId
LIMIT size
);
Run Code Online (Sandbox Code Playgroud)
然后,而不是使用子查询的更新,这应该是连接,因为这些通常在任何版本的 MySQL 中都得到了更好的优化。这是此过程可能比您预期的要慢的另一个可能原因。
UPDATE BATCH_ITEMS BI
JOIN BATCH_ITEMS_UOW UOW ON UOW.ID = BI.ID
SET BI.STATUS = 'PROCESSING';
Run Code Online (Sandbox Code Playgroud)
然后返回结果集...
SELECT * FROM BATCH_ITEMS_UOW;
Run Code Online (Sandbox Code Playgroud)
或者,您可以删除临时表......当您的会话断开连接时,或下次运行此过程时,它将自动删除,因为如果您在同一会话中再次运行它,我们现在会在开始时显式删除它.
DROP TEMPORARY TABLE BATCH_ITEMS_UOW;
Run Code Online (Sandbox Code Playgroud)