【问题标题】:MySQL: Why would putting a partial index on a varchar column make queries SLOWER?MySQL:为什么在 varchar 列上放置部分索引会使查询变慢?
【发布时间】:2012-08-04 01:49:02
【问题描述】:

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

【问题讨论】:

  • 不确定“部分索引”是什么意思。该术语通常涉及不涵盖表的所有行的索引(这是 MySQL 不支持的) - 它通常不用于描述仅索引部分列值的索引。
  • 他的意思是前缀索引,它是一个涵盖CHAR/VARCHAR/*TEXT/*BLOB列的前N个字符的索引。例如,创建索引 my_index(some_field(50)) 来索引 some_field 的前 50 个字符。

标签: mysql sql performance indexing varchar


【解决方案1】:

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

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

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

【讨论】:

  • 这是有道理的,作为一般解释,但我不确定它是否适合我的情况。首先,前 500 个字符是唯一的(我拥有的最长路径是 405 个字符 - 4096 是为了确保我可以处理文件系统上的任何有效路径)。此外,它应该能够单独使用索引来检测匹配,因为模式大小远小于索引的大小。最后,这解释了为什么单次访问和索引速度较慢 - 但索引应该意味着它必须少得多的访问/比较。我的查询只返回几百个,远远小于 400 万个总大小。
  • MySQL 仍然需要查询表,即使搜索字符串小于索引前缀长度。
【解决方案2】:

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

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

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

【讨论】:

    【解决方案3】:

    面罩

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

    原谅我的愚蠢!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-05-25
      • 1970-01-01
      • 2021-04-04
      • 1970-01-01
      • 2016-04-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多