如何改进已经优化的查询需要18秒?

时间:2011-08-06 17:15:53

标签: mysql optimization

所以我有一个512mb ram的vps,以及一个像这样的MySQL表:

CREATE TABLE `table1` (
  `id` int(20) unsigned NOT NULL auto_increment,
  `ts` timestamp NOT NULL default CURRENT_TIMESTAMP,
  `value1` char(31) collate utf8_unicode_ci default NULL,
  `value2` varchar(100) collate utf8_unicode_ci default NULL,
  `value3` varchar(100) collate utf8_unicode_ci default NULL,
  `value4` mediumtext collate utf8_unicode_ci,
  `type` varchar(30) collate utf8_unicode_ci NOT NULL,
  PRIMARY KEY  (`id`),
  KEY `type` (`type`),
  KEY `date` (`ts`)
) ENGINE=MyISAM AUTO_INCREMENT=469692 DEFAULT CHARSET=utf8
  COLLATE=utf8_unicode_ci

如果我执行这样的查询,则需要2~18秒才能完成:

SELECT `id`, `ts`, `value1`, `value2`, `value3` FROM table1 WHERE
`type` = 'something' ORDER BY `id` DESC limit 0,10; 

EXPLAIN SELECT告诉我:

  select_type: SIMPLE
         type: ref
possible_keys: type
          key: type
      key_len: 92
          ref: const
         rows: 7291
        Extra: Using where; Using filesort

我认为'使用filesort'可能是问题,但事实证明 事实并非如此。如果我删除了ORDER BY和LIMIT,那么 查询速度是一样的(我关闭了测试的查询缓存 SET @@query_cache_type=0;)。

mysql> EXPLAIN SELECT `id`,`ts`,`value1`,`value2`, `value3` 
       FROM table1 WHERE `type` = 'something'\G

  select_type: SIMPLE
         type: ref
possible_keys: type
          key: type
      key_len: 92
          ref: const
         rows: 7291
        Extra: Using where

不知道是否重要但行 近似值不准确:

SELECT COUNT(*) FROM table1 WHERE `type` = 'something';

返回22.8k行。

查询似乎已经优化,我不知道如何进一步 改进它。整个表包含370k行,大约4.6 GiB 在尺寸方面。是否有可能因为类型是随机的 它逐行改变(随机分布在整个表中) 只需2~18秒来从磁盘中获取数据?

有趣的是当我使用只有几百行的类型时, 那些查询也很慢。 MySQL以大约100行/秒的速度返回行!

|-------+------+-----------|
| count | time |   row/sec |
|-------+------+-----------|
| 22802 | 18.7 | 1219.3583 |
|    11 |  0.1 |      110. |
|   491 |  4.8 | 102.29167 |
|   705 |  5.6 | 125.89286 |
|   317 |  2.6 | 121.92308 |
|-------+------+-----------|

为什么这么慢?我可以进一步优化查询吗?我应该搬家吗? 数据到较小的表?

我认为自动分区是一个好主意,可以创建一个新的 动态分配每种类型。这是不可能的 很多原因,包括最大分区数是1024,和 可以有任何类型。我也可以尝试应用程序级别 分区,为每种新类型创建一个新表。我不会 我想这样做,因为它引入了极大的复杂性。我不知道我是怎么回事 可以为所有表中的所有行设置唯一ID。另外,如果我到达 多次插入/秒,性能会显着下降。

提前致谢。

4 个答案:

答案 0 :(得分:4)

您需要该查询的多列索引:

KEY `typeid` (`type`, `id`)

不幸的是,正如你所说的那样,如果没有ORDER ,它也很慢,所以它很慢,因为记录分散在磁盘上并且它必须进行大量的搜索。一旦缓存,它应该非常快(注意:22.8 / 370 * 4.6G = 283M,所以如果你做其他活动/查询那些记录将不会在内存中长时间或甚至可能不适合。)。

执行iostat 1验证I / O瓶颈。大量的RAM可以解决您的问题。 SSD也可以解决您的问题。但RAM更便宜;)

答案 1 :(得分:0)

如果您对优化感到绝望,可以尝试重新安排您的餐桌。首先,您从类型中选择并排序每一行,然后将其重写为新表,并将其他类型逐个添加到该表中。我建议进行一种表碎片整理,但我对此没有任何经验。

答案 2 :(得分:0)

有许多方法可以改进查询。在您的情况下,我看到您的索引必须是巨大的,因为负责key_len: 92的索引Unicode VARCHAR(30)列。这是你可以尝试的:用更小的东西替换大的VARCHAR索引。保留type列,但删除索引并创建一个新的索引列typeidx,您可以将其创建为INT UNSIGNED(如果可能,还可以创建SMALLINT)。

创建一个类似于此的表:

CREATE TABLE `typetable` (
  `typeidx` INT UNSIGNED NOT NULL auto_increment,
  `type` varchar(30) collate utf8_unicode_ci NOT NULL,
  PRIMARY KEY  (`typeidx`),
  UNIQUE KEY `type` (`type`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

您填写现有类型

INSERT INTO typetable (type) SELECT DISTINCT type FROM table1;

然后您必须使用类似

的内容更新table1.typeidx
UPDATE table1 t1 JOIN typetable tt USING (type)
   SET t1.typeidx = tt.typeidx

现在您的旧查询可能会变成类似

SELECT `id`,`ts`,`value1`,`value2`, `value3` 
   FROM table1 WHERE `typeidx` = (SELECT typeidx FROM typetable WHERE type = 'something')

当然,您还必须维护typetable并在创建type时插入新值。

答案 3 :(得分:0)

我没有比实现垂直分区更好的主意。我创建了一个没有mediumtext列的相同表,没有这个列就复制了整个表,18秒查询现在只需要100ms!新表只有55mb。