【问题标题】:Simple SELECT query is slow with large table大表的简单 SELECT 查询很慢
【发布时间】:2015-03-05 05:40:18
【问题描述】:

我有一个结构如下的表

SHOW CREATE TABLE data_temperature;

CREATE TABLE `data_temperature` (
  `temperature_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `data_id` bigint(20) unsigned NOT NULL,
  `x_id` varchar(32) DEFAULT NULL,
  `x_sn` varchar(16) DEFAULT NULL,
  `x_unit` char(1) DEFAULT NULL,
  `x_value` decimal(6,2) DEFAULT NULL,
  PRIMARY KEY (`temperature_id`),
  KEY `created` (`created`),
  KEY `data_id` (`data_id`),
  KEY `x_value` (`x_value`)
) ENGINE=InnoDB AUTO_INCREMENT=6274618 DEFAULT CHARSET=latin1

我有一个从这里提取数据的基本查询,这真的很慢。所以我将查询分解为更简单的术语,发现这个非常简单的查询很慢(17.52 秒):

SELECT data_temperature.x_value FROM data_temperature WHERE data_temperature.created BETWEEN '2015-02-02 18:28:42' AND '2015-03-04 18:28:42';

该表有 6,274,617 行。事实上,SELECT COUNT(*) FROM data_temperature 也需要 3.66 秒。

运行此查询的系统是我的开发系统,它是运行 Ubuntu 14.04 的四核、4GB RAM 和固态驱动器。

这是关于运行这样的查询需要多长时间,还是我做错了什么?有没有更高效的数据返回方式?

【问题讨论】:

  • 日期时间字段是否被索引?这将极大地帮助您根据该条件进行搜索
  • @Hanky웃Panky 我以为直到你问。我认为我的问题中的CREATE TABLE 表明它是,但如果你问,也许我读错了。
  • 在查询之前运行 EXPLAIN 以查看您的索引是否被拾取?
  • 不是,虽然我怀疑这是因为我返回了 1848520 行,或者 1848520/6274617 =~ 29% 的表,所以优化器跳过了索引。如果我缩短范围以返回更少的行(例如,一周的价值而不是一个月的价值),那么EXPLAIN 表明它确实使用了索引,但它的运行速度仍然比我预期的要慢得多(1.47 秒)像这样简单的SELECT

标签: mysql mariadb mysql-slow-query-log


【解决方案1】:

想想输出有多少行。想想这些行占用了多少磁盘空间。想想磁盘运行速度有多慢。想想你将如何处理所有这些行。 17 秒是合理的。

同样,由于这些因素,COUNT(*) 需要 3.66 秒。

让我们深入挖掘。

InnoDB 表上的SELECT COUNT(*) FROM tbl 将完全扫描其中一个索引,同时计算行数。索引可能约为 100MB。它必须从磁盘中获取所有 100MB,除了可能所有已经缓存的内容。它必须查看 6M 行中的每一行,并在进行中计数。总计:3.66 秒。

现在让我们看看另一个查询。它更复杂,因此更慢。

你为那个 WHERE 子句有一个很好的索引:INDEX(created)。那是在BTree中。首先,它会在“2015-02-02 18:28:42”或之后找到第一个条目。然后是线性向前扫描,直到它到达'2015-03-04 18:28:42'。这可能需要不到 3.66 秒。但是……

对于索引中的每一项,都需要查找value。它首先找到主键temperature_id,它位于同一个BTree 中,紧邻created。使用temperature_id,它遍历另一个BTree,即有PK和数据的那个,找到对应的行。它在那里找到value。这对范围内的 created 重复。这些查找所需的块将是更多 MB 的数据。

可以使 SELECT 运行得更快,但它只对这一个查询有帮助: INDEX(created, value)。这是一个“覆盖”索引。这意味着 所有 SELECT 所需的列都在索引中找到。因此,它不需要进入另一个 BTree。这可能会导致 3.66 秒或更短的时间。

【讨论】:

  • 感谢您的详细解释。我在这些表上运行的查询数量非常有限,因此创建额外的索引可能是要走的路。但是,正如我所指出的,我在 SSD 上运行它。表大小为 836MB,所以我认为这可以更快地搜索,但你可能是对的。我只是想确保我正在调查所有验证我的查询和结构是否正确的途径。
  • temperature_id 和 data_id 是 BIGINT,每个占用 8 个字节。可能 INT UNSIGNED 会起作用——每个 4 个字节;范围为 4 亿。更小 --> 更快。您的表小于 1GB;可能它完全被缓存了,因此我们谈论的是 CPU 时间,而不是 SSD 速度。
  • 对于大多数应用程序,我通常使用 INT 而不是 BIGINT,但在这种情况下,表可能会增长到超过 42 亿条记录的范围。我将尝试您的覆盖索引的想法,看看是否可以改善这种情况。再次感谢!
猜你喜欢
  • 2021-05-08
  • 1970-01-01
  • 1970-01-01
  • 2021-10-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多