1

我有一个具有以下结构的表

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 和固态驱动器的四核系统。

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

4

1 回答 1

1

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

同样,由于大多数这些因素,COUNT(*) 花费了 3.66 秒。

让我们深入挖掘。

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

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

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

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

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

于 2015-03-05T06:15:03.737 回答