【问题标题】:MySQL query optimization of LIKE term% ORDER BY intMySQL查询优化LIKE term% ORDER BY int
【发布时间】:2012-08-31 01:08:44
【问题描述】:

我的问题是关于在使用前缀匹配时处理与 int COLUMN 结合的 VARCHAR 上的 MySQL 索引。 例如如果我有这样的疑问:

SELECT * FROM tbl WHERE name LIKE 'query%' ORDER BY weight DESC LIMIT 5

考虑到我有一个索引一个名称-> 权重,该索引是否需要找到前缀query 然后 ORDER BY 的所有外观,或者即使使用前缀匹配(% )。我对此感到困扰,因为对于流行的名称(例如 query=john),我可能会发现自己搜索 john 的所有外观很长时间,这将使限制变得无用,并且查询变得缓慢,因为我正在处理拥有庞大的数据集。

【问题讨论】:

    标签: mysql indexing query-optimization


    【解决方案1】:

    假设'query' 的长度等于或短于name 的索引前缀:

    1. (name, weight) 上的复合 BTREE 索引将按 name 然后 weight 排序。从概念上讲:

      +---------+--------+---------+ |姓名(7) |重量 |地址 | +---------+--------+---------+ |查询aa | 500 | 0x1.... | |查询aa | 500 | 0xe.... | |查询aa | 498 | 0x8.... | |查询aa | 491 | 0xb.... | |查询aa |第486章0xc.... | |查询aa | 430 | 0x3.... | |可查询 | 600 | 0x2.... | |可查询 |第592章0x7.... | |可查询 | 550 | 0x4.... | |可查询 | 321 | 0xa.... | |可查询 | 321 | 0x6.... | |可查询 | 304 | 0x9.... | |可查询 | 297 | 0x5.... | |查询bc | 800 | 0xd.... | : : : :
    2. MySQL 可以非常快速地遍历这样的索引,以在过滤器 name LIKE 'query%' 定义的范围内找到每个索引前缀的前 5 个权重(我不确定它是否执行此步骤,但如果它没有):

      +---------+--------+---------+ |姓名(7) |重量 |地址 | +---------+--------+---------+ |查询aa | 500 | 0x1.... | |查询aa | 500 | 0xe.... | |查询aa | 498 | 0x8.... | |查询aa | 491 | 0xb.... | |查询aa |第486章0xc.... | |可查询 | 600 | 0x2.... | |可查询 |第592章0x7.... | |可查询 | 550 | 0x4.... | |可查询 | 321 | 0xa.... | |可查询 | 321 | 0x6.... | |查询bc | 800 | 0xd.... | : : : :
    3. 此时,MySQL 必须对结果执行文件排序:

      +---------+--------+---------+ |姓名(7) |重量 |地址 | +---------+--------+---------+ |查询bc | 800 | 0xd.... | |可查询 | 600 | 0x2.... | |可查询 |第592章0x7.... | |可查询 | 550 | 0x4.... | |查询aa | 500 | 0x1.... | |查询aa | 500 | 0xe.... | |查询aa | 498 | 0x8.... | |查询aa | 491 | 0xb.... | |查询aa |第486章0xc.... | |可查询 | 321 | 0xa.... | |可查询 | 321 | 0x6.... | : : : :
    4. 只有这样它才能使用前 5 个结果从表中获取相关记录:

      +---------+--------+---------+ |姓名(7) |重量 |地址 | +---------+--------+---------+ |查询bc | 800 | 0xd.... | --> 从表中获取 |可查询 | 600 | 0x2.... | --> 从表中获取 |可查询 |第592章0x7.... | --> 从表中获取 |可查询 | 550 | 0x4.... | --> 从表中获取 |查询aa | 500 | 0x1.... | --> 从表中获取 +---------+--------+---------+

    如果'query' 比name 的索引前缀长,那么MySQL 必须在上面的步骤1 中对表进行查找,以便充分过滤随后排序的记录。

    【讨论】:

    • 所以你实际上是说它需要找到与查询匹配的单词的所有外观,让像 'john%' ORDER BY weight LIMIT 5 这样的查询对他来说真的很难吗?
    • @Noam:我是说这取决于索引前缀的长度。
    【解决方案2】:

    您问了另一个问题“创建一个最适合通过 4000 万个名称进行通配符搜索的索引”。好的,你有 4000 万条记录。

    现在考虑以下公式:

    x = COUNT(DISTINCT values in a column) / COUNT(values in a column)
    

    列上的索引更好,x 越接近 1。如果为 1,则所有值都是不同的,没有重复,因此索引非常快。

    现在您正在寻找“john%”。那是4个字母和一个开放式结尾。哪些字母并不重要,您的数据库必须处理 26*26*26*26=456976 个不同的值。把它放在上面的公式和你的 4000 万条记录中。你会得到一个x 0,0114244。

    我不知道阈值是多少,但 IIRC 是 0,1 什么的。所以,如果你的 x 高于 0,1,则使用索引,如果低于,则不使用。

    为什么会这样?使用索引甚至会减慢速度,导致您的数据库必须查看索引,在该索引中查看物理硬盘驱动器上哪个位置的适当记录,然后获取该记录。因此,当 x 低于 10% 时,只进行全表扫描会更快。

    总结一下:只用一个像你这样的弱索引过滤 4000 万条记录根本没有用。

    【讨论】:

    • How MySQL Optimizes WHERE Clauses: "每个表索引都被查询,并且使用最佳索引,除非优化器认为使用表扫描更有效。有一次,扫描是使用基于最佳索引是否跨越超过 30% 的表,但固定百分比不再决定使用索引或扫描之间的选择。优化器现在更加复杂,它的估计基于其他因素,例如表大小、行数和 I/O 块大小。"
    • 感谢eggyal,所以它是0,3,信息有点过时了。无论如何,我认为我的观点仍然有效。
    • 同意,我只是为了澄清而添加。 :)
    • @tombom 你会推荐什么方法?
    • 如果没有关于您的架构、用例等的大量信息,真的很难分辨。也许查找表是哈希索引的一个选项。最简单的方法当然是使用适当的复合索引尽可能多地过滤查询,但要问的问题太多了,考虑到你最好为此问一个不同的问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-05-13
    • 2012-06-12
    • 1970-01-01
    • 1970-01-01
    • 2012-12-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多