对于大表,简单的SELECT查询很慢

时间:2015-03-05 05:40:18

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

我有一个包含以下结构的表

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内存和固态驱动器。

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

1 个答案:

答案 0 :(得分:1)

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

同样,由于大多数因素,COUNT(*)耗时3.66秒。

让我们深入挖掘。

InnoDB表上的

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

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

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

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

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