【问题标题】:Anyone care to help optimize a MySQL query?有人愿意帮助优化 MySQL 查询吗?
【发布时间】:2012-06-10 21:55:14
【问题描述】:

这是查询:

SELECT COUNT(*) AS c, MAX(`followers_count`) AS max_fc, 
       MIN(`followers_count`) AS min_fc, MAX(`following_count`) AS max_fgc,
       MIN(`following_count`) AS min_fgc, SUM(`followers_count`) AS fc,
       SUM(`following_count`) AS fgc, MAX(`updates_count`) AS max_uc,
       MIN(`updates_count`) AS min_uc, SUM(`updates_count`) AS uc
FROM `profiles`
WHERE `twitter_id` IN (SELECT `followed_by` 
                       FROM `relations` 
                       WHERE `twitter_id` = 123);

这两个表是profilesrelations。两者都有超过 1,000,000 行的 InnoDB 引擎。两者都在twitter_id 上有索引,relations 在 (twitter_id, followed_by) 上有一个额外的索引。查询执行时间超过 6 秒,这真的让我很沮丧。我知道我可以以某种方式加入这个,但我的 MySQL 知识不是很酷,这就是我寻求你帮助的原因。

提前谢谢大家=)

干杯, 克~

更新

好的,我设法缩短到 2.5 秒。我使用了 INNER JOIN 并添加了三个索引对。这是解释结果:

id, select_type, table, type, possible_keys, 
    key, key_len, ref, rows, Extra

1, 'SIMPLE', 'r', 'ref', 'relation', 
    'relation', '4', 'const', 252310, 'Using index'

1, 'SIMPLE', 'p', 'ref', 'PRIMARY,twiter_id,id_fc,id_fgc,id_uc', 
    'id_uc', '4', 'follerme.r.followed_by', 1, ''

希望这会有所帮助。

另一个更新

以下是两个表的 SHOW CREATE TABLE 语句:

CREATE TABLE `profiles` (
  `twitter_id` int(10) unsigned NOT NULL,
  `screen_name` varchar(45) NOT NULL default '',
  `followers_count` int(10) unsigned default NULL,
  `following_count` int(10) unsigned default NULL,
  `updates_count` int(10) unsigned default NULL,
  `location` varchar(45) default NULL,
  `bio` varchar(160) default NULL,
  `url` varchar(255) default NULL,
  `image` varchar(255) default NULL,
  `registered` int(10) unsigned default NULL,
  `timestamp` int(10) unsigned default NULL,
  `relations_timestamp` int(10) unsigned default NULL,
  PRIMARY KEY  USING BTREE (`twitter_id`,`screen_name`),
  KEY `twiter_id` (`twitter_id`),
  KEY `screen_name` USING BTREE (`screen_name`,`twitter_id`),
  KEY `id_fc` (`twitter_id`,`followers_count`),
  KEY `id_fgc` (`twitter_id`,`following_count`),
  KEY `id_uc` (`twitter_id`,`updates_count`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

CREATE TABLE `relations` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `twitter_id` int(10) unsigned NOT NULL default '0',
  `followed_by` int(10) unsigned default NULL,
  `timestamp` int(10) unsigned default NULL,
  PRIMARY KEY  USING BTREE (`id`,`twitter_id`),
  UNIQUE KEY `relation` (`twitter_id`,`followed_by`)
) ENGINE=InnoDB AUTO_INCREMENT=1209557 DEFAULT CHARSET=utf8

哇,一团糟 =) 对不起!

【问题讨论】:

    标签: mysql optimization query-optimization


    【解决方案1】:

    连接看起来像这样:

    SELECT COUNT(*) AS c,
    MAX(p.`followers_count`) AS max_fc,
    MIN(p.`followers_count`) AS min_fc,
    MAX(p.`following_count`) AS max_fgc,
    MIN(p.`following_count`) AS min_fgc,
    SUM(p.`followers_count`) AS fc,
    SUM(p.`following_count`) AS fgc,
    MAX(p.`updates_count`) AS max_uc,
    MIN(p.`updates_count`) AS min_uc,
    SUM(p.`updates_count`) AS uc
    FROM `profiles` AS p
    INNER JOIN `relations` AS r ON p.`twitter_id` = r.`followed_by`
    WHERE r.`twitter_id` = 123;
    

    为了帮助优化它,您应该在两个查询上运行 EXPLAIN SELECT ...。

    【讨论】:

    • 这样的连接会减慢速度。
    • 这将执行时间从 6s 减少到 3s
    【解决方案2】:

    创建以下复合索引:

    profiles (twitter_id, followers_count)
    profiles (twitter_id, following_count)
    profiles (twitter_id, updates_count)
    

    并发布查询计划,看在上帝的份上。

    顺便问一下,这个COUNT(*)返回多少行?

    更新:

    您的表格行很长。在您选择的所有字段上创建复合索引:

    profiles (twitter_id, followers_count, following_count, updates_count)
    

    以便JOIN 查询可以从该索引中检索它需要的所有值。

    【讨论】:

    • 谢谢!创建了索引对 id_fc、id_fgc、id_uc。似乎节省了我约 300 毫秒。查询计划?你的意思是解释结果?查看问题更新。
    • COUNT(*) 返回 195436 行。那里有一些错误,where 子句中的未知列 p.twitter_id (我猜是所有错误)。在我将 p 添加到每个 FROM 配置文件后,它在“on 子句”中显示“未知列”“r.followed_by”。不知道下一步该怎么做。非常感谢您的帮助。
    • @kovshenin:至于错误,嗯,对于 200,000 行来说,3 秒是相当不错的时间。您能否为您profilesrelations 发布完整的CREATE TABLE 声明?
    • 嘿,修复了之前的错误。抱歉,我自己没有弄清楚。无论如何,现在我坚持这一点:如果没有 GROUP BY 子句,则混合没有 GROUP 列的 GROUP 列 (MIN(),MAX(),COUNT(),...) 是非法的。我应该分组吗?
    • CREATE TABLE 声明更新了我的帖子。 3 秒是不错的,但还不够,我知道有办法低于这个时间,我发现只是时间问题,哈哈 =) 虽然我可能需要两三个月才能读完那本 MySQL 高性能书我几周前买的。 =))
    【解决方案3】:
    SELECT COUNT(*) AS c,
      MAX(`followers_count`) AS max_fc, MIN(`followers_count`) AS min_fc,
      MAX(`following_count`) AS max_fgc, MIN(`following_count`) AS min_fgc,
      SUM(`followers_count`) AS fc, SUM(`following_count`) AS fgc,
      MAX(`updates_count`) AS max_uc, MIN(`updates_count`) AS min_uc, SUM(`updates_count`) AS uc
    FROM `profiles`
    JOIN `relations`
      ON (profiles.twitter_id = relations.followed_by)
    WHERE relations.twitted_id = 123;
    

    可能会快一点,但您需要测量并检查是否确实如此。

    【讨论】:

    • 我是按照上面的方法去的,但是我觉得这两个没什么区别。
    • 是的,如何/如果你打破线,或表别名和字段限定的差异,并不重要(除非你需要使用别名和/或限定,如果需要消除歧义,但我在这里看不到任何痕迹)。
    【解决方案4】:

    count(*) 在 InnoDB 引擎下是一个非常昂贵的操作,你有没有试过这个查询没有那块?如果它导致最多的处理时间,那么也许您可以保留一个运行值而不是每次都查询它。

    【讨论】:

    • 只有在没有 WHERE 时才真正适用
    • 嗯,以前没听说过,但猜猜是有道理的。
    • 删除 COUNT(*) 并没有改变任何东西 :(
    【解决方案5】:

    我会从程序员的角度来解决这个问题;我将有一个单独的表(或某处的存储区域)存储与原始查询中的每个字段关联的最大值、最小值和总和值,并在每次更新和添加表记录时更新这些值。 (尽管如果处理不当,删除可能会出现问题)。

    在填充这些值的原始查询完成后(这与您发布的查询几乎相同),您实际上是将最终查询减少为从数据表中获取一行,而不是计算所有内容一次。

    【讨论】:

    • 真的和上面的甘道夫想法一样!
    • 您的意思是让TRIGGERS 每次都更新这些值?嗯,可能是个好主意。
    • 是的,自从我使用 mySQL 以来已经有一段时间了,不确定它们是否有触发器,但是是的,这就是一般的想法 :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-05
    • 2011-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-06
    相关资源
    最近更新 更多