优化“重新发布”MySQL 查询

如何解决优化“重新发布”MySQL 查询

我有一个帖子表、一个转帖表和一个表示“用户关注”状态的表。

我想做一些类似 Twitter 的事情,在那里我会显示所有关注用户的帖子或转发。

我希望帖子在第一次出现时出现,这样如果多个用户重新发布帖子,它只会出现在第一次。

为了加快查询速度,我在每次创建帖子时插入到 repost 表中,这样也会创建相应的 repost(来自作者)。

我的架构如下所示:

Table Post
id: INT
userId: INT
time: INT

Table Repost
id: INT
postId: INT
userId: INT
time: INT

Table users_following
userId: INT
followerId: INT

我的查询看起来像这样。

SELECT sr.* FROM Repost sr
INNER JOIN (
    SELECT MIN(ir.time) min_time,ir.postId FROM Repost ir
    WHERE ir.userId IN (
        SELECT uf.userId FROM users_following uf WHERE
        ir.userId = uf.userId AND uf.followerId = 1
    )
    OR ir.userId = 1
    GROUP BY ir.postId
) rr ON rr.postId = sr.postId AND sr.time = rr.min_time

想法是这样的:

  1. SELECT FROM users_following uf。选择查看者后跟的所有用户 ID。
  2. SELECT FROM Repost ir. 选择给定帖子的最短转发时间,其中转发者 ID 是关注的用户或查看者。
  3. SELECT FROM Repost sr. 使用内连接选择给定帖子的最短时间的转发。

这有效,但第 3 阶段很慢。我相信这是因为一旦我们有一个很大的 min_times 列表,我们就不能使用任何索引从该子查询中进行选择,这意味着我们需要扫描所有内容。有没有办法构造此查询以使其性能更高?

这是针对铁杆读者的完整EXPLAINSHOW CREATE TABLE

解释

+----+--------------------+------------+------------+--------+-------------------------------------------------------------+----------------------+---------+---------------------------------+--------+----------+--------------------------+
| id | select_type        | table      | partitions | type   | possible_keys                                               | key                  | key_len | ref                             | rows   | filtered | Extra                    |
+----+--------------------+------------+------------+--------+-------------------------------------------------------------+----------------------+---------+---------------------------------+--------+----------+--------------------------+
|  1 | PRIMARY            | <derived2> | NULL       | ALL    | NULL                                                        | NULL                 | NULL    | NULL                            | 797455 |   100.00 | Using where              |
|  1 | PRIMARY            | sr         | NULL       | ref    | IDX_DA9843F3E094D20D,repost_time_idx,repost_stream_idx      | repost_time_idx      | 4       | rr.min_time                     |      1 |     4.92 | Using where              |
|  2 | DERIVED            | ir         | NULL       | index  | IDX_DA9843F364B64DCC,IDX_DA9843F3E094D20D,repost_stream_idx | IDX_DA9843F3E094D20D | 4       | NULL                            | 797456 |   100.00 | Using where              |
|  3 | DEPENDENT SUBQUERY | uf         | NULL       | eq_ref | PRIMARY,IDX_17C2F70264B64DCC,IDX_17C2F702F542AA03           | PRIMARY              | 8       | prose_2_24_2021.ir.userId,const |      1 |   100.00 | Using where; Using index |
+----+--------------------+------------+------------+--------+-------------------------------------------------------------+----------------------+---------+---------------------------------+--------+----------+--------------------------+

SHOW CREATE TABLE Repost

CREATE TABLE `Repost` (
  `id` int(11) NOT NULL AUTO_INCREMENT,`postId` int(11) NOT NULL,`userId` int(11) NOT NULL,`time` int(11) NOT NULL,`isRepost` int(11) NOT NULL,PRIMARY KEY (`id`),KEY `IDX_DA9843F364B64DCC` (`userId`),KEY `IDX_DA9843F3E094D20D` (`postId`),KEY `repost_time_idx` (`time`),KEY `repost_stream_idx` (`time`,`userId`,`postId`),CONSTRAINT `FK_DA9843F364B64DCC` FOREIGN KEY (`userId`) REFERENCES `ProseUser` (`id`),CONSTRAINT `FK_DA9843F3E094D20D` FOREIGN KEY (`postId`) REFERENCES `Post` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=809018 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci 

SHOW CREATE TABLE users_following

CREATE TABLE `users_following` (
  `userId` int(11) NOT NULL,`followerId` int(11) NOT NULL,PRIMARY KEY (`userId`,`followerId`),KEY `IDX_17C2F70264B64DCC` (`userId`),KEY `IDX_17C2F702F542AA03` (`followerId`),CONSTRAINT `FK_17C2F70264B64DCC` FOREIGN KEY (`userId`) REFERENCES `ProseUser` (`id`),CONSTRAINT `FK_17C2F702F542AA03` FOREIGN KEY (`followerId`) REFERENCES `ProseUser` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci 

编辑

像这样调整查询会产生更快的结果,尽管添加 ORDER BY 会使其变慢。没有 ORDER BY,这个查询很好。

SELECT sr.* FROM Repost sr
INNER JOIN (
    SELECT MIN(ir.time) min_time,ir.postId FROM Repost ir
    INNER JOIN users_following uf ON ir.userId = uf.userId AND uf.followerId = 1
    GROUP BY ir.postId
) rr ON rr.postId = sr.postId AND sr.time = rr.min_time
ORDER BY sr.time desc
LIMIT 10

以下是此查询的说明:

+----+-------------+------------+------------+--------+--------------------------------------------------------------------------------+----------------------+---------+---------------------------+------+----------+----------------------------------------------+
| id | select_type | table      | partitions | type   | possible_keys                                                                  | key                  | key_len | ref                       | rows | filtered | Extra                                        |
+----+-------------+------------+------------+--------+--------------------------------------------------------------------------------+----------------------+---------+---------------------------+------+----------+----------------------------------------------+
|  1 | PRIMARY     | <derived2> | NULL       | ALL    | NULL                                                                           | NULL                 | NULL    | NULL                      |  691 |   100.00 | Using where; Using temporary; Using filesort |
|  1 | PRIMARY     | sr         | NULL       | ref    | IDX_DA9843F3E094D20D,repost_stream_idx,repost_stream2_idx      | repost_stream2_idx   | 8       | rr.min_time,rr.postId     |    1 |   100.00 | NULL                                         |
|  2 | DERIVED     | uf         | NULL       | ref    | PRIMARY,IDX_17C2F702F542AA03                              | IDX_17C2F702F542AA03 | 4       | const                     |  145 |   100.00 | Using index; Using temporary; Using filesort |
|  2 | DERIVED     | ir         | NULL       | ref    | IDX_DA9843F364B64DCC,repost_stream2_idx | IDX_DA9843F364B64DCC | 4       | prose_2_24_2021.uf.userId |    9 |   100.00 | NULL                                         |
|  2 | DERIVED     | rp         | NULL       | eq_ref | PRIMARY,post_spotlight_idx,post_time_idx,post_trending_idx                     | PRIMARY              | 4       | prose_2_24_2021.ir.postId |    1 |    50.00 | Using where                                  |
+----+-------------+------------+------------+--------+--------------------------------------------------------------------------------+----------------------+---------+---------------------------+------+----------+----------------------------------------------+

解决方法

我编写此类排名查询的典型方式是:

select id,postid,userid,time
from
(
  select rp.*,min(time) over (partition by postid) as first_time
  from repost rp
  where userid = 1 
  or userid in (select userid from users_following where followerid = 1)
) numbered
where time = first_time;

有时优化器在使用 OR 时会遇到问题,如果他们认为这样做更快的话,他们看不到他们可以运行两次表。在这种情况下,我们可以用 UNION 提示:

select id,min(time) over (partition by postid) as first_time
  from
  (
    select *
    from repost
    where userid = 1 
    union all
    select *
    from repost
    where userid in (select userid from users_following where followerid = 1)
  ) rp
) numbered
where time = first_time;

曾经 MySQL 因 IN 子句有问题而闻名。我不认为现在是这种情况了。如果 DBMS 确实有问题,您可以使用 EXISTS 代替:

from repost rp
where exists 
(
  select null
  from users_following uf
  where uf.userid = rp.userid 
  and uf.followerid = 1
)

在版本 8 之前的 MySQL 版本中,诸如 MIN OVER 之类的分析函数不可用。在这些版本中,您必须找到每篇文章的最短时间,然后再次阅读表格。一种直接的方式:

select *
from repost
where (postid,time) in
(
  select postid,min(time)
  from repost
  where userid = 1 
  or userid in (select userid from users_following where followerid = 1)
  group by postid
);

在任何情况下,您都希望通过索引快速查找关注的用户。 DBMS 可以免费提供转发用户并检查他们是否被用户 #1 关注,或者获取用户 #1 并找到所有关注的用户。所以我会提供两个索引:

create index idx1 on users_following (userid,followerid);
create index idx2 on users_following (followerid,userid);

然后你想快速找到他们的转发,然后按帖子 ID 分组并按时间排序。索引:

create index idx3 on repost (userid,time);

另一种看待这个问题的方法:如果我们通读整个表格并保留所需用户的行,如果行已经按 postid、time 排序,那就太好了。所以,以防万一:

create index idx3 on repost (postid,time);

用于完整索引扫描。

索引是提供给 DBMS 的。 DBMS 可以接受此提议并使用或不使用索引。我经常做的事情:

  1. 考虑 DBMS 访问表的顺序。
  2. 为这些路由提供索引。
  3. 使用 EXPLAIN 查看我的哪些索引被使用。
  4. 放下其他人。
,

转帖需要对索引进行大修

  PRIMARY KEY (`id`),KEY `IDX_DA9843F364B64DCC` (`userId`),KEY `IDX_DA9843F3E094D20D` (`postId`),KEY `repost_time_idx` (`time`),KEY `repost_stream_idx` (`time`,`userId`,`postId`),

  PRIMARY KEY(postId,userId,time,id),-- `id` is for uniqueness
  INDEX(id)  -- to keep AUTO_INCREMENT happy

(不知道别人有没有用。)

IN ( SELECT ... ) 改为 EXISTS ( SELECT 1 ... )

OR 是性能杀手。用 OR 的一侧对查询计时,然后用另一侧计时。假设这些时间的总和比您当前的时间快,UNION 那些加在一起。如果可行,请简化每个查询。给我看结果;我可能有更多索引建议。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res