MySQL:为什么在varchar列上放置部分索引会使查询失败?

时间:2012-08-04 01:49:03

标签: mysql sql performance indexing varchar

我有一个表我想存储文件路径 - 所以我有一个varchar字段,大小为4096(linux中默认的MAX_PATH大小)。但是,我需要能够对某个目录中的所有文件路径进行查询,所以我在考虑进行如下查询:

SELECT * 
FROM files_table 
WHERE files_table.path LIKE "/my/awesome/dir/%"

当我在我的数据库上运行它时,路径字段为UNINDEXED,大约需要10秒。好吧,我可以看到这需要一段时间,考虑到我的桌子大小约为400万,而且这是一个没有索引的领域。但是,当我索引它时,索引大小为500,查询时间会跳到......大约30秒!

这对我来说似乎很混乱。有没有人对可能导致这种情况的原因有任何想法?


对于那些渴望获得更多数据的人:

作为一些额外的数据 - 我尝试在查询上运行“解释”,并发现它确实是使用我的索引,但它报告key_len只有5!这看起来很奇怪。

另外 - 虽然我想听到我的问题的一个很好的答案(因为我想了解这里发生了什么!),我也对这个想法持开放态度,“我不知道它为什么这样做,但它没关系,因为你真的应该像这样设计你的数据库......“排序。对于那些倾向于这种方式的人,我真正想要做的是构建一个数据库结构来对来自大型网络文件系统的各种(缓存)数据进行查询。我知道只存储一个文件路径可能是接近这个的最天真的方式,但我想我会尝试将它作为第一遍实现,并看看它在哪里。


编辑:

所以,更多挖掘/信息:实际索引是一个多列索引 - 第一个索引是一个int,持有一个batch_id(即,该表保存有关文件系统的缓存信息,因此每个快照都有它自己的batch_id),第二个是路径varchar的部分索引。因此,当EXPLAIN说索引key_len时,它的前4个字节实际上是用于batch_id的 - 这意味着它只有一个单字节索引的路径!

哦,“实际”查询也限制了batch_id,所以看起来更像是这样:

SELECT * 
FROM files_table 
WHERE batch_id=5 
  AND files_table.path LIKE "_globalSoft/my/awesome/dir/%"

其次 - 我数据库中的大部分文件都有一个以“_”开头的路径 - 上面查询中的“_globalSoft”是一个例子。 (是的,路径都是相对的。)因此,如果key_len只有5,那么键中使用的唯一字符可能是前导“_” - 这可以解释为什么它很慢。

当然,这仍然引出了为什么它只使用领先的“_”的问题。在阅读MySQL索引(http://dev.mysql.com/doc/refman/5.0/en/mysql-indexes.html)的文档时,我注意到这一行:

字符串会自动压缩前缀和末尾空间。请参见第13.1.8节“CREATE INDEX语法”。

不幸的是,给定的链接没有提到有关字符串前缀压缩的任何内容,而且我很难找到有关它的大量信息。我发现的信息都是关于MyISAM的,我现在正在使用InnoDB。 (虽然切换到MyISAM可能有意义,因为对于字符串来说它应该更好。)

3 个答案:

答案 0 :(得分:0)

查询速度较慢,因为MySQL总体上需要做更多的IO。索引仅覆盖前500个字符,前500个字符不是很独特。对于前缀索引,MySQL必须匹配前缀,然后获取行以检查完整值是否与前缀值匹配。对于常见前缀,这可能会产生大量额外的随机IO。随机IO比顺序IO成本高得多。如果没有索引,则使用顺序IO完成表的单次传递,并且查询更快。

您可能不希望将MySQL用于此类搜索。查看Sphinx,Solr或其他文本索引技术,并使用“/”作为单词分隔符索引路径。

您还可以将表分成N个较小的表,并在N个表上并行执行全表扫描。

答案 1 :(得分:0)

返回了多少条记录?看起来你可能正在返回大量的记录;而且在一次扫描中扫描数据显然比从索引中逐个扫描数据更有效。

过度简化,使用索引通常涉及三个(实际的缓存)读取任务。一个用于查找排序键列表中的值,该键提供主索引中记录的键;一个查看主索引以查找表中的记录位置;一个找到表中的记录。

此外,谷歌搜索“基数”,看看您的数据和索引的合格程度。

答案 2 :(得分:0)

捂脸

好吧,我是个白痴......问题是我匹配的目录如“ globalSoft” - 即以下划线开头的目录 - 并没有意识到“”是一个特殊的角色(比如%),并没有逃脱它。

原谅我的愚蠢!