3

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

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

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

4

2 回答 2

1

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

现在考虑以下公式:

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

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

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

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

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

总结一下:像你这样只用一个弱索引过滤 4000 万条记录是毫无用处的。

于 2012-09-06T09:18:42.830 回答
1

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

  1. 届时将对 on 上的复合BTREE索引(name, weight)进行排序。从概念上讲:nameweight

    +---------+--------+---------+
    | 姓名(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 可以非常快速地遍历这样的索引,以找到过滤器定义的范围内每个索引前缀的前 5 个权重name LIKE 'query%'(我不确定它是否执行此步骤,但如果它没有执行,我会感到惊讶):

    +---------+--------+---------+
    | 姓名(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 中对表执行查找,以便充分过滤随后排序的记录。

于 2012-09-06T09:21:26.420 回答