问题描述
有点奇怪。我有一个函数一开始运行得非常好,比如说在选择返回值和其他一些列时执行 15 毫秒,获取 10 毫秒。但是如果不断刷新相同的查询,一遍又一遍,查询的执行就会上升。首先是 15 毫秒,然后是 17 毫秒,然后……我一直到 900 毫秒。主要是获取时间增加,但执行也是如此。因此,最后获取时间为 600 毫秒,执行时间为 300 毫秒。知道发生了什么吗?
功能。我只用一个简单的 IF/ELSEIF 进行了试验,但它在性能方面给出了完全相同的结果。
create function get_table(var_account_id int unsigned) returns varchar(20)
reads sql data
BEGIN
RETURN IF(
(SELECT EXISTS(SELECT TRUE
FROM TableA
WHERE account_id = var_account_id
AND expiring_at > CURRENT_TIMESTAMP)),'TableA',IF((SELECT EXISTS(SELECT TRUE
FROM TableB
WHERE account_id = var_account_id
AND expiring_at > CURRENT_TIMESTAMP)),'TableB',IF((SELECT EXISTS(SELECT TRUE
FROM TableC
WHERE account_id = var_account_id
AND expiring_at > CURRENT_TIMESTAMP)),'TableC',IF((SELECT EXISTS(SELECT TRUE
FROM TableD
WHERE account_id = var_account_id
AND expiring_at > CURRENT_TIMESTAMP)),'TableD',NULL)
)));
END;
var_account_id = 1 运行一次后的函数说明
9,SUBQUERY,TableD,ref,"TableD_expiring_at_index,TableD_account_id_index",TableD_account_id_index,4,const,1,100,Using where
7,TableC,"TableC_account_id_index,TableC_expiring_at_index",TableC_account_id_index,5,Using where
5,TableB,"TableB_expiring_at_index,TableB_account_id_index",TableB_account_id_index,9.26,Using where
3,TableA,"TableA_expiring_at_index,TableA_account_id_index",TableA_account_id_index,Using where
在 account_id 和 expiring_at 上设置复合索引根本没有效果
SELECT TableXYZ.*,get_table(TableXYZ.account_id) AS some_value FROM TableXYZ LIMIT 500;
我已经在更复杂的查询上运行过它,但结果总是一样的,一开始很快,重新运行相同的 SELECT 后很慢,假设每秒 5 次,连续 30 秒。即使我让 MysqL 冷却了一会儿,回来,第一次运行仍然是 900 毫秒。而且我相信它会继续上涨。解决此问题的唯一方法是在 Windows 中重新启动 MysqL 服务。
SELECT 的解释:
1,SIMPLE,TableXYZ,ALL,695598,
如果重要的话,我会在 Windows 10 上运行这些,本地。
解决方法
听起来很疯狂。也许你可以避免子查询,这往往会导致性能问题
SELECT X.tabName
FROM (
SELECT 'TableA' as tabName,1 as tabNr,account_id,expiring_at FROM TableA WHERE account_id = var_account_id AND expiring_at > CURRENT_TIMESTAMP
UNION
SELECT 'TableB' as tabName,2 as tabNr,expiring_at FROM TableB WHERE account_id = var_account_id AND expiring_at > CURRENT_TIMESTAMP
UNION
...
) X ORDER BY tabNr LIMIT 1
如果你想避免联合,因为你有非常大的表,那么为什么不在函数中使用控制流?
DECLARE tabName VARCHAR(50);
SET tabName := SELECT 'TableA' FROM TableA WHERE account_id = var_account_id AND expiring_at > CURRENT_TIMESTAMP;
IF (tabName IS NULL) THEN
SET tabName := SELECT 'TableB' FROM TableB WHERE account_id = var_account_id AND expiring_at > CURRENT_TIMESTAMP;
END IF;
and so on...
RETURN tabName;
,
RETURN COALESCE(
IF (EXISTS(...),"A",NULL),IF (EXISTS(...),"B","C","D","E",NULL)
)
其中 ...
是,例如:
SELECT 1
FROM TableA
WHERE account_id = var_account_id
AND expiring_at > CURRENT_TIMESTAMP
注意事项:
- 确保在表*中有此索引:
INDEX(account_id,expired_at)
(按此顺序)。 -
EXISTS()
在找到匹配行的地方停止。 (一个提议的解决方案未能停止,因此需要更长的时间。) -
COALESCE()
不需要评估其所有参数。 (UNION
是。) - (不,我不明白为什么它会变得越来越慢。希望我的公式会一直更快。)