MySQL使用OR左连接速度非常慢
表用户有大约80000条记录 table friends有大约90万条记录 有104条记录的名字为“verena” 此查询(由于其非常简化,查询点消失)非常慢(>20秒): 但是,如果删除联接中的或,则查询是即时的,因此:MySQL使用OR左连接速度非常慢,mysql,performance,join,mariadb,left-join,Mysql,Performance,Join,Mariadb,Left Join,表用户有大约80000条记录 table friends有大约90万条记录 有104条记录的名字为“verena” 此查询(由于其非常简化,查询点消失)非常慢(>20秒): 但是,如果删除联接中的或,则查询是即时的,因此: SELECT users.id FROM users LEFT JOIN friends ON ( users.id = friends.user_id ) WHERE users.firstname = 'verena'; 返回1487个结果或 SELECT us
SELECT users.id FROM users
LEFT JOIN friends ON (
users.id = friends.user_id
)
WHERE users.firstname = 'verena';
返回1487个结果或
SELECT users.id FROM users
LEFT JOIN friends ON (
users.id = friends.friend_id
)
WHERE users.firstname = 'verena';
返回2849个结果
立即执行(0.001s)
如果我把所有的东西都拿走,直接去
SELECT 1 FROM friends WHERE user_id = xxx OR friend_id = xxx
或
这些查询也是即时的
设置了friends.friends\u id、friends.user\u id和users.firstname的索引
我不明白为什么top查询速度很慢,而如果手动将其拆分并执行隔离的语句,那么一切都会很快
我现在唯一的怀疑是,MariaDB首先将所有用户与朋友联系起来,然后才过滤firstname='verena',而不是先过滤firstname='verena',然后将结果与朋友表联系起来,但即使这样,我也不明白为什么删除联接条件中的OR会使它变得很快
我在两台不同的机器上进行了测试,一台运行带Galera集群的MariaDB 10.3.22,另一台运行不带Galera集群的MariaDB 10.4.12
top查询速度如此之慢的技术原因是什么?如何在不将SQL拆分为多个语句的情况下解决这个问题
编辑:
下面是它的解释输出,告诉它没有对friends表使用任何索引,并按照Barmar评论中正确的说明扫描所有记录:
+------+-------------+---------+------+-------------------+-----------+---------+-------+--------+------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+---------+------+-------------------+-----------+---------+-------+--------+------------------------------------------------+
| 1 | SIMPLE | users | ref | firstname | firstname | 768 | const | 104 | Using where; Using index |
| 1 | SIMPLE | friends | ALL | user_id,friend_id | NULL | NULL | NULL | 902853 | Range checked for each record (index map: 0x6) |
+------+-------------+---------+------+-------------------+-----------+---------+-------+--------+------------------------------------------------+
有没有办法让SQL同时使用这两个索引,或者我必须接受这个限制,并使用Barmar的建议来解决这个问题?MySQL通常无法在使用
或连接不同列时使用索引。在联接中,每个表只能使用一个索引,因此如果它使用friends.user\u id
索引,则不会使用friends.friends\u id
,反之亦然
解决方案是执行这两个快速查询,并将它们与UNION
相结合
SELECT users.id FROM users
LEFT JOIN friends ON (
users.id = friends.user_id
)
WHERE users.firstname = 'verena';
UNION
SELECT users.id FROM users
LEFT JOIN friends ON (
users.id = friends.friend_id
)
WHERE users.firstname = 'verena';
在MySQL上,或
通常速度较慢,因为它不能被索引。通常最好是执行两个单独的查询,并将它们与UNION
合并。您能详细说明一下吗?如果正在使用OR,为什么SQL不能处理索引?为什么它不首先在friends.user\u id上使用索引,然后在friends.friends\u id上使用索引?它只能在一个联接中匹配每个表的一个索引。因此,如果它使用用户id
的索引,它必须对朋友id
进行完整扫描,反之亦然。因此,它只是对所有内容进行了全面扫描。感谢您提供的解决方案,但我更感兴趣的是“为什么”而不是解决方案。虽然真正的SQL非常长,但对于UNION SELECT,将其加倍将不是一件很愉快的事,有没有更好的方法不必增加两倍的代码大小?也许您可以在子查询中进行这种联合,并在主查询中执行所有常见的操作。但除此之外,我没有建议,这是MySQL的设计限制。
+------+-------------+---------+------+-------------------+-----------+---------+-------+--------+------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+---------+------+-------------------+-----------+---------+-------+--------+------------------------------------------------+
| 1 | SIMPLE | users | ref | firstname | firstname | 768 | const | 104 | Using where; Using index |
| 1 | SIMPLE | friends | ALL | user_id,friend_id | NULL | NULL | NULL | 902853 | Range checked for each record (index map: 0x6) |
+------+-------------+---------+------+-------------------+-----------+---------+-------+--------+------------------------------------------------+
SELECT users.id FROM users
LEFT JOIN friends ON (
users.id = friends.user_id
)
WHERE users.firstname = 'verena';
UNION
SELECT users.id FROM users
LEFT JOIN friends ON (
users.id = friends.friend_id
)
WHERE users.firstname = 'verena';