Mysql 如何改进耗时18秒的已优化查询?

Mysql 如何改进耗时18秒的已优化查询?,mysql,optimization,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

所以我有一个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; 
解释选择告诉我:

  select_type: SIMPLE
         type: ref
possible_keys: type
          key: type
      key_len: 92
          ref: const
         rows: 7291
        Extra: Using where; Using filesort
我认为“使用文件排序”可能是个问题,但事实证明 事实并非如此。如果我取消订单和限制,则 查询速度是相同的(我关闭查询缓存以进行测试)
SET@@query\u cache\u type=0;

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

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。还有,如果我够到 每秒多次插入,性能将显著下降


提前感谢。

该查询需要多列索引:

KEY `typeid` (`type`, `id`)
不幸的是,正如您所说的,没有顺序也很慢,所以速度很慢,因为记录分散在磁盘上,它必须进行大量查找。一旦缓存,它应该非常快(注意:22.8/370*4.6G=283M,因此如果您进行其他活动/查询,这些记录将不会在内存中很长时间,甚至可能不适合)


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

如果您非常想优化,可以尝试重新安排您的桌子。首先,从一个类型中选择并排序每一行,然后将其重写到一个新表中,并将其他类型逐个添加到该表中。我建议进行一种表格碎片整理,但我没有这方面的经验

改进查询的方法有很多。在您的例子中,我看到您的索引一定有点大,因为索引的Unicode VARCHAR(30)列负责
key\u len:92
。下面是您可以尝试的:将大的VARCHAR索引替换为更小的索引。保留
type
列,但删除索引并创建一个新的索引列
typeidx
,您可以将其创建为无符号整数(或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;
然后您必须更新
表1.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
中的新值。

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

如果只选择索引列,例如“从…选择id”,会发生什么情况?此外,该表是否适合ram,或者是否将从磁盘中获取数千行?什么类型的磁盘?在这种情况下,通常的答案是:确保磁盘速度快,并且所有表都适合ram,这样磁盘就不重要了:)(假设查询是一个简单的选择,并且索引设置正确)仅选择索引列的速度相同,为100行/秒。由于它是一个vps(512mb ram!),我在磁盘上没有控制权或信息,并且没有足够的ram用于数据库的重要部分。@atlau如果只为结果集选择了
type
,会发生什么?如果完全覆盖结果会显著加快查询速度,那么使用完整的覆盖索引可能有助于完成特定的查询。。。。以维护为代价。从何处执行
select
语句?您是直接在服务器上执行,还是通过网络/内联网/VPN/互联网执行?找到一个具有大量RAM的合适VP可能比解决此问题所花费的工程师时间要便宜(即使您解决了此问题,512 RAM下个月您也会遇到另一个问题)为什么/如何帮助?Poster报告说,即使删除
ORDER BY
,速度也是一样的。我已经在我的开发机器上尝试了多列索引,但没有发现任何速度改进(不过没有在服务器上尝试)。是的,问题可能是因为磁盘正在查找,正如我在问题中所说的。在512MB中缓存370k大小的记录…?;)不,先生,今天不行。@pst:您只需要内存中的工作集,我不知道该数据库的使用模式。但是,对于数据库服务器来说,512M是毫无意义的。这是MyISAM,因此任何DDL都将创建表的副本,一个简单的
ALTER table table1 ORDER BY type
也应该这样做。当然,这并不意味着如果磁盘没有足够的连续可用空间,新表就不会被分割。DDL的定义是什么?唯一的一点是将密钥长度从92缩短?是的。对于
INT
,键长度为4;对于
MEDIUMINT
键长度为3;对于
SMALLINT
键长度为2。或者,您可以查看哪些不需要您更改
选择
查询,但每次都需要更新
枚举
的新值
SELECT `id`,`ts`,`value1`,`value2`, `value3` 
   FROM table1 WHERE `typeidx` = (SELECT typeidx FROM typetable WHERE type = 'something')