两个索引良好的表的单个 INNER JOIN 需要超过一分钟才能运行
Posted
技术标签:
【中文标题】两个索引良好的表的单个 INNER JOIN 需要超过一分钟才能运行【英文标题】:Single INNER JOIN of two well-indexed tables takes more than a minute to run 【发布时间】:2018-06-04 09:59:02 【问题描述】:我有一个查询需要大约 90 秒才能运行,即使表应该有正确的索引。我不明白为什么。
我使用的是 mysql,表是 InnoDB。
这是查询:
SELECT count(*)
FROM `following_lists` fl INNER JOIN users u
ON fl.user_uuid = u.user_uuid
WHERE fl.following_query_id = 1000010 AND u.status <= 2
我希望这个查询从表 following_lists
开始,根据 WHERE 条件抓取大约 4K 条记录,通过主键将这些记录连接到表 users
,检查用户表中字段的值,并返回结果记录的计数。为什么需要这么长时间?难道是因为我加入表格的两个字段是 CHAR(40) 而不是整数?
这些是涉及的表及其索引:
CREATE TABLE `users` (
`user_uuid` CHAR(40) NOT NULL,
`status` TINYINT UNSIGNED NOT NULL,
...
PRIMARY KEY (`user_uuid`),
...
)
CREATE TABLE `following_lists` (
`following_id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`following_query_id` INT UNSIGNED NOT NULL,
`user_uuid` CHAR(40) NOT NULL,
PRIMARY KEY (`following_id`),
KEY `query_id` (`following_query_id`),
KEY `user_uuid` (`user_uuid`)
)
这是解释查询的输出:
+----+-------------+-------+--------+--------------------+----------+---------+--------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+--------------------+----------+---------+--------------+------+-------------+
| 1 | SIMPLE | fl | ref | query_id,user_uuid | query_id | 4 | const | 3718 | |
| 1 | SIMPLE | u | eq_ref | PRIMARY | PRIMARY | 160 | fl.user_uuid | 1 | Using index |
+----+-------------+-------+--------+--------------------+----------+---------+--------------+------+-------------+
更多细节:
following_lists
表有大约 25k 行,但只有 3718 行有fl.following_query_id = 1000010
。
表 users
有大约 160k 行,但在连接中应该只选择 3718。只有 40 条记录同时满足这两个条件 fl.following_query_id = 1000010 AND u.status <= 2
。
即使我删除条件AND u.status <= 2
,查询也很慢。
【问题讨论】:
如果是 CHAR(40),我不会感到惊讶。对于整数值,需要进行 1 次比较;使用该 CHAR(40),最多需要进行 40 次比较。实际需要多少取决于 CHAR(40) 中的实际值,如果这些值的前 X 个字符都相同,则至少需要那么多。另外,如果我没记错的话,MySQL 的 CHAR 索引也不会覆盖整个字符串,如果是前导字符,只会覆盖一定的数字。 两列是否使用相同的排序规则和字符集? 查看其他 SO 问题的已接受答案。它可能会为您提供一些关于使用 UUID 提高性能的方法的线索。 ***.com/questions/2365132/uuid-performance-in-mysql/… @SamM 是的,两个表都使用CHARACTER SET=utf8mb4 COLLATE=utf8mb4_unicode_ci
。感谢您的链接,我认为问题肯定出在 UUID 上。当我有时间时,我可能会尝试使用 auto_increment 字段复制数据库,看看问题是否仍然存在。
【参考方案1】:
“有正确的索引”——死的放弃。
如果您使用的是 MyISAM,请不要。相反,切换到 InnoDB。
您需要following_lists.id
吗? (following_query_id, user_uuid)
是唯一的吗?如果是这样,请将它们设为PRIMARY KEY
。
如果以上都做不到,就改
KEY `query_id` (`following_query_id`)
到
INDEX(following_query_id, user_uuid)
UUIDs
效率极低,尤其是当不必要地声明utf8mb4
或CHAR
的大小超出必要时。更改为CHAR(36) CHARACTER SET ascii
。 (注意 `EXPLAIN 中的“160”显着缩小。)
更多关于 UUID 不利于性能的原因:http://mysql.rjweb.org/doc.php/uuid
你有多少内存? innodb_buffer_pool_size
的设置是什么? (听起来太低了。)
有关索引的更多信息:http://mysql.rjweb.org/doc.php/index_cookbook_mysql
【讨论】:
以上是关于两个索引良好的表的单个 INNER JOIN 需要超过一分钟才能运行的主要内容,如果未能解决你的问题,请参考以下文章
DQL查询数据----联表查询(left/right/inner join 区别)
DQL查询数据----联表查询(left/right/inner join 区别)
如何从跨两个 DATE 列的 INNER JOIN 创建 MIN 和 MAX 日期列,其中每个 DATE 列来自单独的表 BigQuery