我无法找出查询速度极慢的原因;在配备 48GB DDR3 的 Dual Xeon L5630 上运行 60 秒,运行 Ubuntu 16.04、PHP7.0-FPM 和 MariaDB 10.0.27
SELECT v.video_id, v.user_id, v.title, v.slug, v.rating, v.rated_by,
v.duration, v.thumb, v.total_views, v.total_comments, v.add_time,
v.view_time, v.status, v.source_id, v.orientation, v.thumbs,
v.featured, v.flagged,
u.username,
s.name,
f.reason,
GROUP_CONCAT(c.name) AS categories
FROM video AS v
LEFT JOIN video_flags AS f ON (f.video_id = v.video_id)
LEFT JOIN video_sources AS s ON (s.source_id = v.source_id)
LEFT JOIN user AS u ON (u.user_id = v.user_id)
LEFT JOIN video_category AS vc ON (vc.video_id = v.video_id)
LEFT JOIN video_categories AS c ON (c.category_id = vc.category_id) GROUP BY v.video_id ORDER BY v.video_id DESC LIMIT 10
我已确定问题出在 video_flags 表中,因为当我注释 f.reason 字段和 video_flags 上的左连接时,查询只需要 152 毫秒。 video_flags 表在 video_id 上有一个索引,并且两个表中的字段类型相同 INT(11)
当我运行解释选择时,我得到以下信息:
+------+-------------+-------+--------+---------------+----------+---------+----------------------------+---------+-------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+-------+--------+---------------+----------+---------+----------------------------+---------+-------------------------------------------------+
| 1 | SIMPLE | v | ALL | NULL | NULL | NULL | NULL | 1219933 | Using temporary; Using filesort |
| 1 | SIMPLE | f | ALL | video_id | NULL | NULL | NULL | 1 | Using where; Using join buffer (flat, BNL join) |
| 1 | SIMPLE | s | eq_ref | PRIMARY | PRIMARY | 4 | adb_network.v.source_id | 1 | |
| 1 | SIMPLE | u | eq_ref | PRIMARY | PRIMARY | 4 | adb_network.v.user_id | 1 | |
| 1 | SIMPLE | vc | ref | video_id | video_id | 4 | adb_network.v.video_id | 2 | Using index |
| 1 | SIMPLE | c | eq_ref | PRIMARY | PRIMARY | 4 | adb_network.vc.category_id | 1 | Using where |
+------+-------------+-------+--------+---------------+----------+---------+----------------------------+---------+-------------------------------------------------+
我不知道我在这里错过了什么,首先我认为它与 video_flags 表为空有关,然后我添加了一条记录并且查询很快(200ms),但现在问题又回来了,查询需要很长时间才能再次完成。
非常感谢任何帮助。
更新:为 @somnium 添加了不带 f.reason 列的解释选择:
+------+-------------+-------+--------+---------------+----------+---------+----------------------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+-------+--------+---------------+----------+---------+----------------------------+------+-------------+
| 1 | SIMPLE | v | index | NULL | PRIMARY | 4 | NULL | 5 | |
| 1 | SIMPLE | f | ref | video_id | video_id | 4 | adb_network.v.video_id | 1 | Using index |
| 1 | SIMPLE | s | eq_ref | PRIMARY | PRIMARY | 4 | adb_network.v.source_id | 1 | |
| 1 | SIMPLE | u | eq_ref | PRIMARY | PRIMARY | 4 | adb_network.v.user_id | 1 | |
| 1 | SIMPLE | vc | ref | video_id | video_id | 4 | adb_network.v.video_id | 2 | Using index |
| 1 | SIMPLE | c | eq_ref | PRIMARY | PRIMARY | 4 | adb_network.vc.category_id | 1 | Using where |
+------+-------------+-------+--------+---------------+----------+---------+----------------------------+------+-------------+
解决方案:按照@somnium的建议,我尝试在video_id
列上添加FORCE INDEX
,这将查询时间从60 秒到 272 毫秒 - 仍然不确定为什么它会在连接期间丢失索引,但问题已解决。谢谢
SELECT v.video_id, v.user_id, v.title, v.slug, v.rating, v.rated_by,
v.duration, v.thumb, v.total_views, v.total_comments, v.add_time,
v.view_time, v.status, v.source_id, v.orientation, v.thumbs,
v.featured, v.flagged,
u.username,
s.name,
f.reason,
GROUP_CONCAT(c.name) AS categories
FROM video v
LEFT JOIN video_flags f FORCE INDEX FOR JOIN (video_id) ON (f.video_id = v.video_id)
LEFT JOIN video_sources s ON (s.source_id = v.source_id)
LEFT JOIN user u ON (u.user_id = v.user_id)
LEFT JOIN video_category vc ON (vc.video_id = v.video_id)
LEFT JOIN video_categories c ON (c.category_id = vc.category_id) GROUP BY v.video_id ORDER BY v.video_id DESC LIMIT 10
最佳答案
您不小心对大型表视频
进行了全表扫描。可以找到潜在问题的列表 at the MySQL documentation .
潜在问题
缺少键
在没有 f.reason 的情况下查看您的解释,优化器将忽略 video_flags
表。这使得 MySQL/MariaDB 能够充分利用所有索引。
添加f.reason
时,MySQL现在需要匹配v.video_id = f.video_id
。由于video_flags
只有一行,MySQL将尝试检索video
中每个条目的v.video_id
。您似乎没有 v.video_id
索引。因此MySQL必须从磁盘/内存中扫描完整的videos
表才能获取video_id
。这会导致检索 1219933 行(相比之下,在没有 video_flags
的 explain select
中检索 5 行)。
低基数
另一个潜在的问题是基数低,但我不太确定到底是什么导致优化器搞砸了。
来自 MySQL 文档:
You are using a key with low cardinality (many rows match the key value) through another column. In this case, MySQL assumes that by using the key it probably will do many key lookups and that a table scan would be faster.
我的理解是,由于 video_flags
中的基数非常低(1-2 个值),因此可能会导致 MySQL 因左侧而查找 videos
上的完整表加入(您始终需要左侧的所有值)。此时它决定全表扫描更好。在您使用 video_id
的其他情况下,不会发生这种情况,因为基数更高。您可以使用 FORCE INDEX
语法强制使用索引。
潜在的解决方案
尝试在 v.video_id
上添加索引以加快查找速度。仔细检查两个 explain selects
以查找哪些索引突然不再使用。
请注意慢速选择中表 v
的 possible_keys
的 NULL
。
尝试使用FORCE INDEX
。
希望有帮助。
关于php - 特定表上的 LEFT JOIN 速度极慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39745267/