【问题标题】:Query for Highest Rated Video (Likes / Dislikes) SLOW when using ORDER使用 ORDER 时查询评分最高的视频(喜欢/不喜欢)慢
【发布时间】:2014-01-22 22:43:39
【问题描述】:

(使用 MySQL)我有一个视频表(简化):

+---------+-------------+-------------+
| videoID |  videoName  | videoAuthor |
+---------+-------------+-------------+
|       1 | cool_video1 | rocky       |
|       2 | mixingTest2 | sensable    |
+---------+-------------+-------------+

还有一个视频评分表,即每次用户喜欢或不喜欢视频时,都会在 videoRatings 表中添加/更新一行: 例如,这意味着 videoID 为 1 的视频有两个喜欢和一个不喜欢。 “1”是 videoRatings 表中的一个。 “2”是不喜欢(简化)

+---------------+---------+-------------------+
| videoRatingID | videoID | videoRatingTypeID |
+---------------+---------+-------------------+
|           121 |       1 |                 1 |
|           234 |       1 |                 1 |
|           290 |       1 |                 2 |
+---------------+---------+-------------------+

现在,很简单,我要做的就是从大约 100,000 个视频中获取得分最高的视频。
自然,我会这样做:

SELECT Videos.videoID,
             COUNT(CASE WHEN videoRatingTypeID =1 THEN 1 ELSE NULL END) AS likes,
             COUNT(CASE WHEN videoRatingTypeID =2 THEN 1 ELSE NULL END) AS dislikes
      FROM Videos
      LEFT JOIN VideoRatings ON VideoRatings.videoID = Videos.videoID
      GROUP BY Videos.videoID
ORDER BY likes DESC

但是这个查询运行大约半秒。这让我担心,当视频表达到 >100 万时,这会更长。 videoRatings 表非常小(约 40 行),video 表约 100,000 行。

我的 videoID 索引显然在 Videos 表中,并且我的 videoRatings 表中的 videoID、videoRatingID 索引和 videoID+videoRatingID 的复合索引

我没有找到更好的方法来做到这一点。我已经阅读了几篇关于将订单移到外面的帖子。但是当我这样做时:

SELECT * FROM (
SELECT Videos.videoID,
             COUNT(CASE WHEN videoRatingTypeID =1 THEN 1 ELSE NULL END) AS likes,
             COUNT(CASE WHEN videoRatingTypeID =2 THEN 1 ELSE NULL END) AS dislikes
      FROM Videos
      LEFT JOIN VideoRatings
      GROUP BY Videos.videoID
) tmp
ORDER BY tmp.likes DESC

改善为零。

处理此布局或此查询的更好方法是什么?谢谢!

【问题讨论】:

  • 您使用的是哪个数据库? MySQL 还是 SQL Server?
  • 对不起,编辑说,MySQL
  • 如果您需要快速响应,那么我建议您有一个喜欢和不喜欢的汇总表,它使用更新/插入/删除触发器或直接通过现在插入喜欢的存储过程进行维护和不喜欢。
  • 因为您的 COUNT 是计算值,并且您按这些计算值的计数进行排序,我认为您正在阻止使用索引,并强制扫描和排序。跨度>
  • @GordonLinoff,我想过这样做,基本上每次触发喜欢/不喜欢时都会更新喜欢或得分列。所以这将是一个近乎即时的查找(只是按分数排序,没有连接)......但是,我希望还有其他我可以做的事情或者我缺少的其他事情,而不是添加那个分数列。我怀疑这是否真的是最好的方法。

标签: mysql sql performance sql-order-by


【解决方案1】:

为了真正的可扩展性,我认为您需要一个维护汇总表的解决方案。与此同时,这可能会更快:

select v.videoID,
       (select count(*)
        from VideoRatings vr
        where vr.videoID = v.videoID and
              videoRatingTypeId = 1
       ) as likes,
       (select count(*)
        from VideoRatings vr
        where vr.videoID = v.videoID
              videoRatingTypeId = 2
       ) as dislikes
from Videos v;

确保您在VideoRatings(videoId, videoRatingTypeId) 上有一个索引(实际上,类型 id 在索引中并不那么重要,但它可以提供帮助)。

这用索引扫描和小型聚合替换了整个视频和评级集上的group by。只要videoRatings 的索引适合内存,这就会扩大。

编辑:

您的视频评分表非常简陋,包含的信息仅包含喜欢和不喜欢的汇总数量。例如,这样的表可能包含评分的日期/时间以及进行评分的人。

但是。您正在将inserting 行的新评级添加到此表中。好吧,用信息对另一个表(可能是videos)进行update 几乎相同的操作。然后将您当前的表视为历史日志。

使用updates 的好处是您可以在日志变大时截断日志。现在,您必须从一开始就为每个视频保留所有评分。

【讨论】:

  • 当您说摘要表时,您会建议在视频行本身中存储一个分数列并更新它吗?或者创建一个表格,例如“VideoScores”,并在 VideoID 列中添加一个 videoScore(likes, etc) 列。这可能超出了这个问题的范围,但这是存储此类数据时的普遍做法吗?我想这种“抢最高分”已经实施了无数次,我很难相信他们是这样做的,看起来太复杂了,也许我错了。
  • 查询没有提供任何加速(或减速),它几乎在同一时间运行。我也有你提到的综合指数。我应该指定我将始终需要保持收视率表完整(我永远无法真正截断它),因为用户需要查看他们喜欢和不喜欢的视频。这是喜欢/不喜欢的常见主题。例如,在堆栈溢出时,如果他们删除了您“向上”某个答案的日志,那么当您返回该页面时,箭头将不再是橙色的。这些日志必须永久保存
  • @AugieGardner 。 . .尝试将视频分级表缩放到其当前大小的 100 倍或 1000 倍。任何时间都以阅读视频表为主。
  • 我可以做到,但视频表最终会增长到 > 100 万,我假设这会影响性能?我现在只是想知道将分数保留在视频行本身是否是人们这样做的最佳方式。在这种情况下,保持评论计数就足够了,这似乎很奇怪
  • @AugieGardner 。 . .如果您有多个计数,我倾向于将它们存储在单独的“统计”表中,主键与 videos 表相同。逻辑分离是有道理的。预先汇总常用数据是一种常见的性能增强技术。
猜你喜欢
  • 2013-05-26
  • 1970-01-01
  • 2014-03-30
  • 1970-01-01
  • 2012-01-14
  • 2014-12-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多