【问题标题】:How can I optimize a query in a large MySQL table without any joins?如何在没有任何连接的情况下优化大型 MySQL 表中的查询?
【发布时间】:2014-12-16 22:47:17
【问题描述】:

如何优化来自单个大型表(约 75M 行)的单个查询?

SELECT
    log_id
FROM
    score
WHERE
    class_id IN (17,395)
ORDER BY date_reverse
LIMIT 10000;

我为一组特定的类提取最近的 10k 条记录,以便在更大的导入脚本中快速知道它们是否已经存在。

我认为我的索引已正确,但此查询持续 5-50 秒!

如果您还需要什么,请告诉我。

EXPLAIN
    SELECT
        log_id
    FROM
        score
    WHERE
        class_id IN (17,395)
    ORDER BY date_reverse
    LIMIT 10000;

*** row 1 ***
          table:  score
           type:  range
  possible_keys:  class_id,score_multi_2,class_id_date_reverse,score_multi_5
            key:  class_id_date_reverse
        key_len:  4
            ref:  NULL
           rows:  1287726
          Extra:  Using where; Using index; Using filesort

CREATE TABLE `score` (
  `log_id` bigint(20) NOT NULL,
  `profile_id` bigint(20) DEFAULT NULL,
  `date` datetime DEFAULT NULL,
  `class_id` int(11) NOT NULL,
  `score` float(10,6) DEFAULT NULL,
  `score_date` datetime DEFAULT NULL,
  `process_date` datetime DEFAULT NULL,
  `status_type_id` int(3) NOT NULL DEFAULT '0',
  `date_reverse` int(11) DEFAULT NULL,
  UNIQUE KEY `unique_key` (`log_id`,`class_id`),
  KEY `class_id` (`class_id`),
  KEY `profile_id` (`profile_id`),
  KEY `date` (`date`),
  KEY `score` (`score`),
  KEY `status_type_id` (`status_type_id `),
  KEY `status_type_id_date` (`status_type_id`,`date`),
  KEY `class_status_type_id_date_log_id` (`class_id`,`status_type_id`,`date`,`log_id`),
  KEY `date_reverse` (`date_reverse`),
  KEY `class_id_date_reverse` (`class_id`,`date_reverse`),
  KEY `date` (`date`),
  KEY `class_id_date_reverse_log_id` (`class_id`,`date_reverse`,`log_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

【问题讨论】:

    标签: mysql sql query-optimization


    【解决方案1】:

    我的猜测是,运行此查询的最快方法是硬着头皮允许对 20,000 行进行排序。我想到的查询是:

    SELECT *
    FROM ((SELECT log_id
           FROM score
           WHERE class_id = 17
           ORDER BY date_reverse
           LIMIT 10000
          ) UNION ALL
          (SELECT log_id
           FROM score
           WHERE class_id = 395
           ORDER BY date_reverse
           LIMIT 10000
          )
         ) s
    ORDER BY date_reverse
    LIMIT 10000;
    

    对于此查询,您需要score(class_id, date_reverse, log_id) 上的复合索引。每个子查询都应该非常有效地使用这个索引。但是,最终的排序将需要使用文件排序。

    【讨论】:

    • 我怀疑 Gordon 是对的,但我也可以尝试其他方法。将您的唯一键更改为主键并将顺序反转为 (class_id,log_id`)。然后尝试像以前一样运行查询。假设这是在 Innodb 存储引擎上,MySQL 将在每个二级索引中包含主键值,使您的 date_reverse 索引有效(class_id,log_id)+ date_reverse。我不确定它是否会对优化器产生影响,但它可能会。我至少会好奇地试一试。
    • 谢谢@evanv,你的意思是这样吗:ALTER TABLE score DROP UNIQUE KEY, ADD PRIMARY KEY (class_id,log_id);
    • @Ryan,是的。同样,我不确定它是否会有所帮助。但我会试一试。另外,如果您尝试一下,请将您的 class_id 键更改为 log_id。
    • 最终,Gordon 和 evan 提出的解决方案对结果时间几乎没有影响。我接受 Gordon 的回答是因为我最终归档了该表的很大一部分以使其正常工作,并且不再有能力测试其他解决方案。
    猜你喜欢
    • 1970-01-01
    • 2012-08-01
    • 2021-07-07
    • 2014-07-26
    • 2020-03-18
    • 1970-01-01
    • 2011-05-18
    • 2011-11-13
    • 1970-01-01
    相关资源
    最近更新 更多