【发布时间】:2012-09-03 11:09:48
【问题描述】:
我有一个大型 MySQL、MyISAM 表,大约有 400 万行,运行在一个核心 2 双核、8G RAM 笔记本电脑上。
此表共有 30 列,包括 varchar、decimal 和 int 类型。
我在 varchar(16) 上有一个索引。让我们将此列称为:“indexed_varchar_column”。
我的查询是
SELECT 9 columns FROM the_table WHERE indexed_varchar_column = 'something';
它总是为我查询的每个“东西”返回大约 5000 行。
查询的解释返回:
+----+-------------+-------------+------+----------------------------------------------------+--------------------------------------------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------------+------+----------------------------------------------------+--------------------------------------------+---------+-------+------+-------------+
| 1 | SIMPLE | the_table | ref | many indexes including indexed_varchar_column | another_index NOT: indexed_varchar_column! | 19 | const | 5247 | Using where |
+----+-------------+-------------+------+----------------------------------------------------+--------------------------------------------+---------+-------+------+-------------+
首先我不确定为什么选择 another_index。实际上,它选择了一个索引,该索引是 indexed_varchar_column 和另外 2 列(构成所选列的一部分)的复合索引。也许这是有道理的,因为不必读取查询中的 2 列可能会使事情变得更快。真正的问题是以下一个:
对于我匹配的每个“东西”,查询需要 5 秒。第二次我查询“某事”需要 0.15 秒(我猜是因为查询正在被缓存)。当我对“something_new”运行另一个查询时,又需要 5 秒。所以,它是一致的。
问题是:我发现创建一个索引(另一个复合索引,包括我的 indexed_varchar_column)并再次删除它会产生针对新“something_other”的所有进一步查询只需 0.15 秒。请注意 1) 我创建了一个索引 2) 再次删除它。所以一切都处于相同的状态。
我猜想构建和删除索引所需的所有操作都会使 SQL 引擎缓存某些东西,然后再重用。当我在所有这些之后对查询运行 EXPLAIN 时,我得到的结果与以前完全相同。
如何继续了解创建-删除索引过程中缓存的内容,以便在不操作索引的情况下对其进行缓存?
更新:
根据 Marc B 的评论,建议当 mySQL 创建索引时,它会在内部执行 SELECT... 我尝试了以下操作:
SELECT * FROM my_table;
花费了 30 秒并返回了 400 万行。好消息是所有进一步的查询再次非常快(直到我重新启动系统)。请注意,重新启动后查询又变慢了。我猜这是因为 mySQL 正在使用某种操作系统缓存。
有什么想法吗?如何显式缓存我猜的表?
更新 2: 或许我应该提到这张表可能严重碎片化。它有 400 万行,但我会定期删除很多旧字段。我还添加了新的。由于我每天的 ID (删除的行)有很大的差距,我删除了主索引 (ID) 并用连续的数字再次创建它。该表可能会非常碎片化,因此 IO 一定是个问题......不知道该怎么办。
【问题讨论】:
-
在内部构建索引基本上是“选择字段,in,index from ...”来获取构建索引所需的数据,因此只需构建索引即可为您缓存数据索引,即使您自己没有运行任何选择。
-
感谢 Marc B。这给了我一个想法。我在 400 万行上运行了一个疯狂的“SELECT * FROM my_table”。我在控制台上做了这个并丢弃了所有结果。我花了一段时间。创建索引所需的相同时间,大约。所有进一步的查询都是超快的。有没有其他优雅的方式达到同样的效果?我会用这个发现更新问题。
-
所以你有两个包含
indexed_varchar_column的索引:一个是{indexed_varchar_column, other_column1, other_column2}上的复合索引。其他指数如何?它是复合的吗?其中列的顺序是什么? -
布兰科,你可以忘记索引。一个简单的 SELECT * FROM my_table 缓存所有内容。我相信我在硬盘上有一个 IO 瓶颈(我需要查询的列太多,覆盖索引太大)。当我运行 SELECT * 时,IO 瓶颈得到缓解。我想我需要明确地缓存表。