Mysql 如何加速慢速SQL查询

Mysql 如何加速慢速SQL查询,mysql,sql,optimization,Mysql,Sql,Optimization,我需要从列表中获得10个有标签的创意,它们的作者没有被禁止,并且不在当前用户看到的创意中 现在,我的查询如下所示: Table ideas: id, author_id, some_columns Table ideas_tags: idea_id, tag_name Table ideas_seen: idea_id, user_id Table user: uid, ban, some_columns 这是网站上最慢的查询,我不知道如何加快速度 说明: 不要使用TINYTEXT;

我需要从列表中获得10个有标签的创意,它们的作者没有被禁止,并且不在当前用户看到的创意中

现在,我的查询如下所示:

Table ideas: id, author_id, some_columns

Table ideas_tags: idea_id, tag_name

Table ideas_seen: idea_id, user_id

Table user: uid, ban, some_columns
这是网站上最慢的查询,我不知道如何加快速度

说明:

  • 不要使用
    TINYTEXT
    ;更改为
    VARCHAR(255)
    ,并去掉索引上的“前缀”。也就是说,将
    索引电子邮件(255)
    更改为
    索引(电子邮件)

  • 不要索引“标志”,这样的索引将不会被使用,因为它们没有用处。示例:
    已删除

  • 不要让一个索引成为另一个索引的“左”部分。示例
    主键(id)
    vs
    索引(id…)
    。如果是
    主键
    ,请保留;扔掉另一个,因为它没有提供额外的好处

  • 我认为没有必要加入
    idea\u标签两次;看看你能不能避免

  • 该查询存在“充气-放气”综合征。首先,它使用
    JOINs
    增加行数,然后使用
    groupby
    精确返回原始行(少一些被过滤掉的行)。在这样做的过程中,庞大的
    ideas.
    被拖到临时表中

  • TEXT
    (包括
    TINYTEXT
    )会阻止对tmp表更有效地使用
    内存

让我们浏览一下充气和放气的消除过程

首先,让我们构造外部部分:

CREATE TABLE IF NOT EXISTS `ideas` (
`id` int(11) NOT NULL,
  `tutorial` tinyint(4) NOT NULL,
  `text` text NOT NULL,
  `author_id` int(11) NOT NULL,
  `active` bit(1) NOT NULL,
  `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `views` int(11) NOT NULL,
  `views_all` int(11) DEFAULT '0',
  `deleted` tinyint(4) NOT NULL DEFAULT '0',
  `many_users` tinyint(4) DEFAULT NULL,
  `game_id` int(11) DEFAULT NULL
) ENGINE=InnoDB AUTO_INCREMENT=35983 DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS `ideas_seen` (
`id` int(11) NOT NULL,
  `idea_id` int(11) NOT NULL,
  `user_id` int(11) NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=3694368 DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS `ideas_tags` (
`id` int(11) NOT NULL,
  `idea_id` int(11) NOT NULL,
  `tag_name` tinytext NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=86832 DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS `users` (
  `uid` int(11) NOT NULL,
  `email` tinytext,
  `password_hash` tinytext,
  `restore_code` tinytext NOT NULL,
  `last_action` timestamp NULL DEFAULT NULL,
  `score` float NOT NULL,
  `date_register` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `posts_length` int(11) DEFAULT NULL,
  `settings` text,
  `titles` int(11) NOT NULL DEFAULT '1',
  `filter` int(11) NOT NULL DEFAULT '1',
  `note` text NOT NULL,
  `ban` tinyint(4) DEFAULT NULL,
  `mod_send` smallint(6) DEFAULT '0',
  `mod_get` int(11) DEFAULT '0',
  `fp_notified` int(11) NOT NULL DEFAULT '0',
  `skilled` tinyint(4) NOT NULL DEFAULT '0',
  `show_only_skilled` tinyint(4) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


ALTER TABLE `ideas`
 ADD PRIMARY KEY (`id`), ADD KEY `author_id` (`author_id`), ADD KEY `game_id` (`game_id`), ADD KEY `active` (`active`), ADD KEY `many_users` (`many_users`), ADD KEY `deleted` (`deleted`), ADD KEY `tutorial` (`tutorial`), ADD FULLTEXT KEY `idea_text` (`text`);

ALTER TABLE `ideas_seen`
 ADD PRIMARY KEY (`id`), ADD KEY `user_id` (`user_id`), ADD KEY `idea_id` (`idea_id`);

ALTER TABLE `ideas_tags`
 ADD PRIMARY KEY (`id`), ADD KEY `tag_name` (`tag_name`(255)), ADD KEY `idea_id` (`idea_id`);

ALTER TABLE `users`
 ADD PRIMARY KEY (`uid`), ADD UNIQUE KEY `email` (`email`(255)), ADD KEY `ban` (`ban`), ADD KEY `fp_notified` (`fp_notified`), ADD KEY `skilled` (`skilled`);


ALTER TABLE `ideas`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=35983;
ALTER TABLE `ideas_seen`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=3694368;
ALTER TABLE `ideas_tags`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=86832;

ALTER TABLE `ideas`
ADD CONSTRAINT `ideas_ibfk_1` FOREIGN KEY (`game_id`) REFERENCES `games` (`id`) ON DELETE NO ACTION;

ALTER TABLE `ideas_seen`
ADD CONSTRAINT `ideas_seen_ibfk_1` FOREIGN KEY (`idea_id`) REFERENCES `ideas` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION;

ALTER TABLE `ideas_tags`
ADD CONSTRAINT `ideas_tags_ibfk_2` FOREIGN KEY (`idea_id`) REFERENCES `ideas` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION;
假设我们可以填写
,我们现在就有了一条更快的评估之路。这需要在
id
上建立一个索引,您可以使用
主键(id)
。如果运气好,(并且没有
WHERE
),只需要触摸10行。(在您的版本中,必须对整个表进行收集、分组、排序,然后才能交付10个。)

由于您所有的
联接
都是
左联接
,因此可以证明,与
ideas
以外的表接触的
WHERE
子句不会过滤掉任何行。剩下的

SELECT ideas.*, ( ??? ) as tags
    FROM ideas
    WHERE ???
    ORDER BY ideas.id DESC
    LIMIT 10;
为此,让我们(尽管我不确定它是否会被使用):

返回到作为标记的
。。。现在剥离查询,仅获取
标记\u name
值,以使
组\u CONCAT
符合给定的
想法。id

INDEX(active, deleted, author_id)
(这就是为什么有两个连接到
idea\u标签
)的原因。同时,我建议
SELECT
可以作为获取
标签
的子查询

嗯。。那怎么办

SELECT GROUP_CONCAT(DISTINCT IT_V.tag_name SEPARATOR '|||') AS tags
    FROM       ideas_tags IT_V  ON ideas.id = IT_V.idea_id
      AND  (      IT.tag_name = 'some_tag'
              OR  IT.tag_name = 'another_tag'
              OR  IT.tag_name IS NULL
           )
这似乎不进行过滤,因为它是
左侧的
。它似乎没有提供任何列,因为
用户
在其他地方没有提到。所以,我不得不假设这是浪费代码

同上

LEFT JOIN  users ON users.uid = ideas.author_id 
WHERE ( users.ban=0  OR  users.ban IS NULL )
因此,它归结为删除一些索引,添加一个索引,然后将查询重写为

LEFT JOIN  ideas_seen IV    ON ideas.id = IV.idea_id
                 AND  IV.user_id=145974517
WHERE   IV.id IS NULL

很可能,
不同的
是不必要的。

您没有向我们展示您的索引是什么,以及您的数字是什么样的查询中使用的每一列的索引。什么是数字?这是不够的信息。发布您的show create Table如果删除了所有的
左侧
,您会得到不同的结果集吗?如果是这样的话,我认为这两个子句是在互相争斗:
author\u id=145974517
IV.id为空
LEFT JOIN  users ON users.uid = ideas.author_id 
WHERE ( users.ban=0  OR  users.ban IS NULL )
LEFT JOIN  ideas_seen IV    ON ideas.id = IV.idea_id
                 AND  IV.user_id=145974517
WHERE   IV.id IS NULL
SELECT  ideas.*, 
        ( SELECT  GROUP_CONCAT(DISTINCT IT_V.tag_name SEPARATOR '|||')
            FROM  ideas_tags IT_V
            WHERE  ideas.id = IT_V.idea_id
              AND  (   IT.tag_name = 'some_tag'
                   OR  IT.tag_name = 'another_tag'
                   OR  IT.tag_name IS NULL ) 
        ) as tags
    FROM  ideas
    WHERE  author_id!=145974517
      AND  active=1
      AND  deleted=0
    ORDER BY  ideas.id DESC
    LIMIT  10;