所以我有一个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。另外,如果我到达 多次插入/秒,性能会显着下降。
提前致谢。
答案 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。