【问题标题】:Slow MySQL order by datetime column按日期时间列缓慢的 MySQL 排序
【发布时间】:2020-02-21 03:08:21
【问题描述】:

我有这样定义的新闻表:

CREATE TABLE `news` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `creation_date` datetime DEFAULT NULL,
  `modification_date` datetime DEFAULT NULL,
  `active` bit(1) DEFAULT NULL,
  `mark_for_delete` bit(1) DEFAULT NULL,
  `verified` bit(1) DEFAULT NULL,
  `bot_id` int(11) DEFAULT NULL,
  `description` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
  `hash` varchar(100) NOT NULL,
  `published_at` datetime DEFAULT NULL,
  `source` varchar(255) DEFAULT NULL,
  `title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
  `url` varchar(511) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `UK_1dmji5m90xaiy84vttgkvsub2` (`hash`),
  KEY `index_news_source` (`source`),
  KEY `index_news_creation_date` (`creation_date`)
) ENGINE=InnoDB AUTO_INCREMENT=30887718 DEFAULT CHARSET=latin1

还有一个用于标记属于某些流行名称的新闻的连接表:

CREATE TABLE `star_news` (
  `stars_id` bigint(20) NOT NULL,
  `news_id` bigint(20) NOT NULL,
  PRIMARY KEY (`stars_id`,`news_id`),
  KEY `FK4eqjn8at6h4d9335q1plxkcnl` (`news_id`),
  CONSTRAINT `FK1olc51y8amp8op1kbmx269bac` FOREIGN KEY (`stars_id`) REFERENCES `star` (`id`),
  CONSTRAINT `FK4eqjn8at6h4d9335q1plxkcnl` FOREIGN KEY (`news_id`) REFERENCES `news` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

这是我返回最新消息的查询

SELECT DISTINCT n.* 
FROM news n 
JOIN star_news sn 
  ON n.id = sn.news_id 
WHERE sn.stars_id IN (1234, 12345) 
ORDER BY n.creation_date DESC 
LIMIT 2;

解释:

+----+-------------+-------+------------+--------+-------------------------------------+---------+---------+-----------------------+------+----------+-----------------------------------------------------------+
| id | select_type | table | partitions | type   | possible_keys                       | key     | key_len | ref                   | rows | filtered | Extra                                                     |
+----+-------------+-------+------------+--------+-------------------------------------+---------+---------+-----------------------+------+----------+-----------------------------------------------------------+
|  1 | SIMPLE      | sn    | NULL       | range  | PRIMARY,FK4eqjn8at6h4d9335q1plxkcnl | PRIMARY | 8       | NULL                  |196225|   100.00 | Using where; Using index; Using temporary; Using filesort |
|  1 | SIMPLE      | n     | NULL       | eq_ref | PRIMARY                             | PRIMARY | 8       | cosmos_dev.sn.news_id |    1 |   100.00 | NULL                                                      |
+----+-------------+-------+------------+--------+-------------------------------------+---------+---------+-----------------------+------+----------+-----------------------------------------------------------+

这个查询在我的机器上需要 20 秒。如果我删除 order by 子句,它会在亚毫秒内返回。如何通过运行速度更快下订单?

我尝试在 creation_date 上使用强制索引,因为它是索引列,但它降低了性能

【问题讨论】:

  • 问:根据您将运行的正常查询类型,在这种情况下,您正在执行 2 个 stars_id 值(1234、12345),如果您不应用按 creation_date 降序限制 2。
  • ~196k(来自解释结果)
  • 我认为不可能找到一个简单的“add-this-index”解决方案。此类索引不存在,因为您使用一个索引读取记录,并且需要按其他表中的其他字段对结果进行排序。
  • @akuzminsky 我想我可以在 star_news 中添加一个名为 news_date 的新列,并在 (news_id, news_date) 上创建一个索引。
  • 您需要使用相同的索引进行阅读和排序。那么它会很快。所以,sn 表中必须是 (stars_id, news_date)。

标签: mysql sql innodb


【解决方案1】:

首先,将查询写成:

SELECT n.* 
FROM news n 
WHERE EXISTS (SELECT 1
              FROM star_news sn 
              WHERE n.id = sn.news_id AND
                    sn.stars_id IN (1234, 12345) 
             )
ORDER BY n.creation_date DESC
LIMIT 2 ;

这消除了外部SELECT DISTINCT,这应该会有所帮助。

然后,在star_news(news_id, stars_id) 上创建一个索引。这也可以利用news(creation_date desc, id) 上的索引。

【讨论】:

  • 但是这变成了一个依赖子查询,除非新写两篇关于1234或12345的文章,否则会很慢。
  • @AndreasWederbrand 。 . .不必要。相关子查询通常是查询的最快实现。我完全不知道为什么这么多人不理解这一点。
  • 如果优化器选择通过creation_date扫描n,它可能会卡住检查30887718行。
【解决方案2】:

那么您有 196,000 篇与这 2 颗星相关的新闻文章?额外的解释告诉你发生了什么:

在哪里使用;使用索引;使用临时的;使用文件排序

MySQL 正在创建一个临时文件并对其进行排序以满足 order by,因为它无法使用有助于连接和文章按日期排序的索引。

【讨论】:

  • 添加了 comp 索引。同样的解释计划,新索引上的强制索引也没有帮助..
  • 那无济于事。查询按一个表 (sn) 中的字段读取记录,并按另一表 (n) 中的字段排序。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-12-03
  • 2013-10-28
  • 1970-01-01
  • 2015-09-12
  • 2010-12-01
  • 1970-01-01
  • 2010-12-15
相关资源
最近更新 更多