mariadb 简单JOIN较慢,使用STRAIGHT_JOIN时非常快,但并非适用于所有情况

juzqafwq  于 2022-11-23  发布在  其他
关注(0)|答案(2)|浏览(189)

要将一个视频放在一个或多个类别中,我有以下表格:

  • Video (id, url, title, viewCount)~1,000,000行
  • VideoCategory (id, videoId, categoryId~6,000,000行
  • Category (id, name)~200行

VideoCategory(categoryId, videoId)上的索引
Category(name)上的唯一索引
以下查询在"汽车"类别中获取10个观看次数最多的视频的速度太慢(~5.5秒)。"汽车"类别包含200,000个视频。

SELECT v.* FROM Video v
  JOIN VideoCategory vc ON vc.videoId = v.id
  JOIN Category c ON vc.categoryId = c.id
  WHERE c.name = 'Cars' 
  ORDER BY v.viewCount DESC
LIMIT 10

当查询一个只包含100个视频的类别时,需要大约0.05秒。
EXPLAIN "汽车"类别的查询。

+------+-------------+-------+--------+------------------------------------+--------------------+---------+-----------------------+--------+----------------------------------------------+
| id   | select_type | table | type   | possible_keys                      | key                | key_len | ref                   | rows   | Extra                                        |
+------+-------------+-------+--------+------------------------------------+--------------------+---------+-----------------------+--------+----------------------------------------------+
|    1 | SIMPLE      | c     | const  | name_UNIQUE                        | name_UNIQUE        | 322     | const                 | 1      | Using index; Using temporary; Using filesort |
|    1 | SIMPLE      | vc    | ref    | fk_Category_idx,category_video_idx | category_video_idx | 8       | const                 | 493988 | Using index                                  |
|    1 | SIMPLE      | v     | eq_ref | PRIMARY                            | PRIMARY            | 8       | VideoDB.vc.videoId    | 1      | Using where                                  |
+------+-------------+-------+--------+------------------------------------+--------------------+---------+-----------------------+--------+----------------------------------------------+

我怎样才能加快速度(希望是0.1秒或更少)?忽略ORDER BY有很大的影响,但它显然没有给我想要的结果。

    • 更新**

我尝试了一下STRAIGHT_JOIN,并观察到下面的查询有一些有趣的地方:

SELECT v.* FROM Video v
STRAIGHT_JOIN VideoCategory vc ON v.id = vc.videoId
WHERE vc.categoryId = (SELECT id FROM Category WHERE name = 'Cars')
ORDER BY v.viewCount ASC
LIMIT 10

它在0.011秒内返回!我还删除了JOIN Category c并将其替换为WHERE vc.categoryId = (SELECT id FROM Category WHERE name = 'Cars'),如果Category.name不存在,则立即返回0个结果。
不幸的是,仅仅添加STRAIGHT_JOIN并不能解决所有问题,它只对超过1000个视频的类别比较快,视频越多,它看起来就越快。对于少于100个视频的类别,它会变得非常慢,去掉STRAIGHT_JOIN会让它再次变得非常快。
对于一个如此简单的查询,我期望计划者找到最优路径。这是怎么回事?
另一个观察结果是,将顺序从ASC更改为DESC将使某些类别的查询再次变慢,但对其他类别则不会变慢(例如,ASC将花费0.01秒,DESC将花费0.8秒)。

rqmkfv5c

rqmkfv5c1#

对于20万条记录来说,这种排序方式会花费很多时间。一个想法是创建一个定期更新的前10个表,这样你就可以只显示新的前10个表中的条目,这对用户来说会更快,数据库加载也更容易。

ngynwnxp

ngynwnxp2#

原始查询的最佳解决方案是在Category.id中查找“汽车”,这已经完成。
然后,由于VideoCategory上缺少索引,查询计划福尔斯。
我假设查找是从categoryId-〉videoId开始的,那么应该有一个categoryId索引。但是理想情况下,作为一个连接表,(categoryId, videoId)可能是最合适的组合主键,可以改进您的查询。如果需要从videoId-〉categoryId进行反向解析,次索引键videoId。主索引键已经包含在次索引键的结尾。
viewCount的最后一个问题是一个困难的问题,一些查询优化(如rowid_filter)可能会有所帮助。

相关问题