-1

我有一个要存储文件路径的表 - 所以我有一个 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 可能是有意义的,因为它应该使用字符串更好。)

4

3 回答 3

0

面盘

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

原谅我的愚蠢!

于 2012-08-06T21:23:38.773 回答
0

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

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

您还可以将表拆分为 N 个较小的表,并对 N 个表进行并行全表扫描。

于 2012-08-04T02:16:57.050 回答
0

有多少记录返回?看来您可能返回了相当大比例的记录;显然,一次扫描数据比从索引中一个一个地挑选出来更有效。

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

另外,谷歌搜索“基数”,看看你的数据和索引有多好。

于 2012-08-06T18:38:15.727 回答