MySQL查询优化LIKE术语%ORDER BY int

时间:2012-09-06 08:47:06

标签: mysql indexing query-optimization

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

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

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

2 个答案:

答案 0 :(得分:1)

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

现在考虑以下公式:

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

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

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

我不知道再次达到什么门槛,但是IIRC是0,1或者其他什么。因此,如果您x高于0,1则使用索引,如果它更低,则不是。

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

总结一下:过滤4000万条只有一个弱指数的记录就像你的一样简直无用。

答案 1 :(得分:1)

如果'query'的长度等于或短于name的索引前缀:

  1. BTREE上的(name, weight)综合name索引将按weight排序name LIKE 'query%'。从概念上讲:

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

    +---------+--------+---------+
    | name(7) | weight | address |
    +---------+--------+---------+
    | queryaa |    500 | 0x1.... |
    | queryaa |    500 | 0xe.... |
    | queryaa |    498 | 0x8.... |
    | queryaa |    491 | 0xb.... |
    | queryaa |    486 | 0xc.... |
    | queryab |    600 | 0x2.... |
    | queryab |    592 | 0x7.... |
    | queryab |    550 | 0x4.... |
    | queryab |    321 | 0xa.... |
    | queryab |    321 | 0x6.... |
    | querybc |    800 | 0xd.... |
    :         :        :         :
    
  3. 此时,MySQL必须对结果执行文件排序:

    +---------+--------+---------+
    | name(7) | weight | address |
    +---------+--------+---------+
    | querybc |    800 | 0xd.... |
    | queryab |    600 | 0x2.... |
    | queryab |    592 | 0x7.... |
    | queryab |    550 | 0x4.... |
    | queryaa |    500 | 0x1.... |
    | queryaa |    500 | 0xe.... |
    | queryaa |    498 | 0x8.... |
    | queryaa |    491 | 0xb.... |
    | queryaa |    486 | 0xc.... |
    | queryab |    321 | 0xa.... |
    | queryab |    321 | 0x6.... |
    :         :        :         :
    
  4. 只有这样才能使用前5个结果从表中获取相关记录:

    +---------+--------+---------+
    | name(7) | weight | address |
    +---------+--------+---------+
    | querybc |    800 | 0xd.... |  --> fetch from table
    | queryab |    600 | 0x2.... |  --> fetch from table
    | queryab |    592 | 0x7.... |  --> fetch from table
    | queryab |    550 | 0x4.... |  --> fetch from table
    | queryaa |    500 | 0x1.... |  --> fetch from table
    +---------+--------+---------+
    
  5. 如果name 更长而不是{{1}}的索引前缀,那么MySQL必须在上面的步骤1中对表进行查找,以便充分过滤记录随后下令。