【问题标题】:What is the fastest way to get user specific data along with a query?获取用户特定数据和查询的最快方法是什么?
【发布时间】:2020-10-16 23:24:11
【问题描述】:

我正在开发一个 QnA 应用程序,在该应用程序中我加载了用户特定的数据,例如用户是否对帖子进行了投票,或者是否关注了撰写帖子的用户。我目前的方法如下-

SELECT tbl_answer.*, tbl_user.username, tbl_user.dp_62, tbl_personal_info.full_name, tbl_personal_info.tagline,
IFNULL(followers.following, 0) following,
IFNULL(voters.voted, 0) voted
FROM tbl_answer
LEFT JOIN tbl_user ON tbl_user.user_id = tbl_answer.user_id
LEFT JOIN tbl_personal_info ON tbl_personal_info.user_id = tbl_answer.user_id
LEFT JOIN (
    SELECT answer_id, IF(a_vote_up = 1, 1, a_vote_down * -1) AS voted 
        FROM tbl_answer_vote
        WHERE user_id = $user_id //Retrieved from the token beforehand
    ) voters ON voters.answer_id = tbl_answer.answer_id
LEFT JOIN (
    SELECT following_id, 1 AS following 
        FROM tbl_user_follow 
        WHERE follower_id = $user_id
    ) followers ON followers.following_id = tbl_answer.user_id
WHERE question_id = ?
ORDER BY selected DESC, 
under_review ASC, 
answer_up_vote DESC, 
answer_id ASC, 
answer_down_vote ASC
LIMIT 30

它似乎没有那么快,有时需要超过 4 秒。答案表有近一百万个条目。 在最短的时间内达到这种结果的一般过程是什么? 如果有任何需要考虑的因素,我会在我的 API 后端使用 Laravel。

感谢您的指导。

【问题讨论】:

  • 问题是您只有单列索引,而 where 条件将由多列索引提供。 MySQL 试图通过使用索引合并来弥补,但这并不像使用单个索引那样高效。
  • “一般流程”是为了避免不必要的工作。为什么在这里使用子查询?你的实际问题是什么?如果您真的要求“一般流程”,我将结束投票,因为“需要更多的 fokus”。
  • 我用 IF-EXISTS 替换了关于投票、关注和书签的连接,这将时间减少到毫秒。在这里发布之前应该尝试过。
  • 在考虑该查询之前,我们需要知道哪个列在哪个表中。您必须提供资格和/或SHOW CREATE TABLE。它对WHERE question_id = ? ORDER BY selected DESC, under_review ASC, answer_up_vote DESC, answer_id ASC, answer_down_vote ASC 产生了很大的不同

标签: mysql sql laravel performance


【解决方案1】:

带有子查询的 LEFT JOIN 与带有未索引表的 JOIN 一样好。也有例外,子查询将生成带有索引的内部临时表。这里可能不是这样。

但是 - 在您的情况下不需要子查询。您所需要的只是一个常规的 LEFT JOIN。只需在 ON 子句中包含$user_id 的条件即可。您的查询应如下所示:

SELECT ...
FROM tbl_answer
LEFT JOIN tbl_user ON tbl_user.user_id = tbl_answer.user_id
LEFT JOIN tbl_personal_info ON tbl_personal_info.user_id = tbl_answer.user_id

-- no subqueries necessary here
LEFT JOIN tbl_answer_vote
  ON  tbl_answer_vote.answer_id = tbl_answer.answer_id
  AND tbl_answer_vote.user_id   = $user_id
LEFT JOIN tbl_user_follow 
  ON  tbl_user_follow.following_id = tbl_answer.user_id
  AND tbl_user_follow.follower_id  = $user_id

WHERE ...
...

【讨论】:

  • 我确实尝试过这种方法,但对速度没有帮助,不知道为什么,虽然它看起来很简单。
【解决方案2】:

回答我自己的问题...连接上的子查询是这里的瓶颈。用 IF-EXISTS 替换它们显着提高了速度,现在平均需要 300 毫秒。

SELECT
    Q.*,
...
...
    IF(EXISTS (SELECT NULL FROM tbl_question_vote WHERE user_id = 642 AND question_id = Q.question_id), 1, 0) voted,
    IF(EXISTS (SELECT NULL FROM tbl_question_bookmark WHERE user_id = 642 AND question_id = Q.question_id), 1, 0) bookmarked,
    IF(EXISTS (SELECT NULL FROM tbl_user_follow WHERE follower_id = 642 AND following_id = Q.user_id), 1, 0) following
LEFT JOIN tbl_user ON tbl_user.user_id = Q.user_id
LEFT JOIN tbl_personal_info ON tbl_personal_info.user_id = Q.user_id
WHERE
    under_review = 0 AND ads = 1
ORDER BY pinned DESC, prioritize DESC, question_id DESC
LIMIT 30

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-11-19
    • 1970-01-01
    • 1970-01-01
    • 2011-01-21
    • 1970-01-01
    • 2011-08-11
    • 1970-01-01
    相关资源
    最近更新 更多