mysql - ORDER BY 主键超级慢,即使子查询非常快
问题描述
create table account_match
(
id int(11) unsigned auto_increment primary key,
smaller_account_id int(11) unsigned not null,
bigger_account_id int(11) unsigned not null,
match_origin varchar(50) null,
created timestamp default CURRENT_TIMESTAMP not null,
constraint account_match_account_id_fk
foreign key (smaller_account_id) references account (id)
on delete cascade,
constraint account_match_account_id_fk_2
foreign key (bigger_account_id) references account (id)
on delete cascade
create index account_match_smaller_account_id_bigger_account_id_index
on account_match (smaller_account_id, bigger_account_id);
);
create table private_message
(
id int(11) unsigned auto_increment primary key,
sender_account_id int(11) unsigned not null,
receiver_account_id int(11) unsigned null,
message text null,
viewed tinyint(1) default 0 not null,
created timestamp default CURRENT_TIMESTAMP not null,
constraint private_message_account_id_fk
foreign key (sender_account_id) references account (id)
on delete cascade,
constraint private_message_account_id_fk_2
foreign key (receiver_account_id) references account (id)
on delete cascade
);
create index private_message_receiver_account_id_sender_account_id_index
on private_message (receiver_account_id, sender_account_id);
create index private_message_sender_account_id_receiver_account_id_index
on private_message (sender_account_id, receiver_account_id);
SELECT account_match.*
FROM (
SELECT account_match.id
FROM account_match
WHERE id IN (SELECT id
FROM account_match
WHERE smaller_account_id = 1
UNION ALL
SELECT id
FROM account_match
WHERE bigger_account_id = 1)
AND NOT EXISTS(SELECT TRUE
FROM private_message
WHERE sender_account_id = smaller_account_id
AND receiver_account_id = bigger_account_id)
AND NOT EXISTS(SELECT TRUE
FROM private_message
WHERE sender_account_id = bigger_account_id
AND receiver_account_id = smaller_account_id)
) match_ids
JOIN account_match using (id)
ORDER BY id DESC
LIMIT 20
1 PRIMARY account_match index PRIMARY PRIMARY 4 20 100 Backward index scan
1 PRIMARY account_match eq_ref PRIMARY PRIMARY 4 miliar.account_match.id 1 100 Using where
6 DEPENDENT SUBQUERY private_message ref private_message_receiver_account_id_sender_account_id_index,private_message_sender_account_id_receiver_account_id_index private_message_sender_account_id_receiver_account_id_index 9 miliar.account_match.bigger_account_id,miliar.account_match.smaller_account_id 1 100 Using index
5 DEPENDENT SUBQUERY private_message ref private_message_receiver_account_id_sender_account_id_index,private_message_sender_account_id_receiver_account_id_index private_message_sender_account_id_receiver_account_id_index 9 miliar.account_match.smaller_account_id,miliar.account_match.bigger_account_id 1 100 Using index
3 DEPENDENT SUBQUERY account_match eq_ref PRIMARY,account_match_smaller_account_id_bigger_account_id_index PRIMARY 4 func 1 26.57 Using where
4 DEPENDENT UNION account_match eq_ref PRIMARY,account_match_smaller_account_id_bigger_account_id_index,account_match_account_id_fk_2 PRIMARY 4 func 1 5 Using where
查询的重点是获取 ID 为 1 的帐户的匹配项(当然其他时候将是其他 ID),其中两个用户都没有互相发送消息。主查询(子查询)在不到 20 毫秒内完成,但是一旦我添加 ORDER BY id DESC,它就会上升到 20 秒。子查询返回了大约 710k 行,但是大小无关紧要,因为我试图按主键排序,对吧?有什么办法可以解决这个速度?
解决方案
试试下面的看看(因为我发现你的大部分代码似乎已经过时了)
以下没有工作
SELECT *
FROM account_match
WHERE (smaller_account_id = 1 or bigger_account_id = 1)
AND EXISTS(SELECT TRUE
FROM private_message
WHERE (sender_account_id != bigger_account_id or receiver_account_id != smaller_account_id)
AND (sender_account_id != smaller_account_id or receiver_account_id != bigger_account_id))
ORDER BY id DESC
LIMIT 20
尝试这个
-- Create a Temp table and get data from the faster query into that.
CREATE TEMPORARY TABLE a_m_temp
SELECT account_match.id
FROM account_match
WHERE id IN (SELECT id
FROM account_match
WHERE smaller_account_id = 1
UNION ALL
SELECT id
FROM account_match
WHERE bigger_account_id = 1)
AND NOT EXISTS(SELECT TRUE
FROM private_message
WHERE sender_account_id = smaller_account_id
AND receiver_account_id = bigger_account_id)
AND NOT EXISTS(SELECT TRUE
FROM private_message
WHERE sender_account_id = bigger_account_id
AND receiver_account_id = smaller_account_id)
-- Inner Join the temp. table to the main
Select a.* from
account_match a
INNER JOIN a_m_temp t
ON a.id = t.id
Order by a.id
Limit 20
推荐阅读
- c - 为什么在 C 中使用 int 格式将输入转换为 char 会改变另一个 char 变量的值?
- node.js - 无法导入 openzeppelin node_modules:不支持文件导入回调
- python - 从 ASP.NET 使用批处理文件调用 Python (anaconda)
- javascript - 这个 noflo 组件有问题吗?
- reactjs - 不明白为什么我的 JSX 元素的动态连接不起作用
- mongoose - 在 mongoose 中访问和比较 2 个模式的键
- vb.net - My.settings 不适用于新表单,之前绑定到其他表单上的 My.Settings 无法更改(灰显)
- flutter - 数据未添加到 Flutter 的列表中
- javascript - 如果我们改变 setTimeout 和 setInterval 的顺序,为什么会额外执行 setInterval 的函数?
- r - 如何使用 R Shiny 中的传单地图修复“在没有活动反应上下文的情况下不允许操作”错误?