类似术语%ORDER BY int的MySQL查询优化

类似术语%ORDER BY int的MySQL查询优化,mysql,indexing,query-optimization,Mysql,Indexing,Query Optimization,我的问题是在使用前缀匹配时,如何处理VARCHAR上的MySQL索引和int列。 e、 g.如果我有这样的疑问: SELECT * FROM tbl WHERE name LIKE 'query%' ORDER BY weight DESC LIMIT 5 考虑到我有一个索引one name->weight,该索引是否需要查找前缀query的所有外观,然后按顺序排序,或者即使使用前缀匹配(%),他是否仍将交叉计算索引化。我对此感到困扰,因为对于流行名称(例如query=john),我可能会发现

我的问题是在使用前缀匹配时,如何处理VARCHAR上的MySQL索引和int列。 e、 g.如果我有这样的疑问:

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

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

你问了另一个问题“创建一个最适合通过4000万个名称进行通配符搜索的索引”。好的,您有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
为00114244

我不知道阈值是多少,但IIRC是0,1或其他。所以,如果你
x
高于0,1,则使用索引,如果它较低,则不使用

为什么会这样?使用索引甚至会减慢速度,因为您的数据库必须查看索引,查看该索引中物理硬盘上的哪个位置有合适的记录,然后获取该记录。因此,当x低于10%时,只需扫描整个表就可以加快速度


总而言之:只使用一个像您这样的弱索引过滤4000万条记录是毫无用处的。

前提是
'query'
的长度等于或短于
name
的索引前缀:

  • (名称、权重)
    上的复合
    b树
    索引将按
    名称
    排序,然后按
    权重
    排序。概念上:

    +---------+--------+---------+ | 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.... | : : : :
  • 此时,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.... | : : : : +---------+--------+---------+ |姓名(7)|体重|地址| +---------+--------+---------+ |querybc | 800 | 0xd| |queryab | 600 | 0x2| |queryab | 592 | 0x7| |queryab | 550 | 0x4| |queryaa | 500 | 0x1| |queryaa | 500 | 0xe| |克雷亚| 498 | 0x8| |queryaa | 491 | 0xb| |克雷亚| 486 | 0xc| |queryab | 321 | 0xa| |queryab | 321 | 0x6| : : : :
  • 只有这样,它才能使用前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 +---------+--------+---------+ +---------+--------+---------+ |姓名(7)|体重|地址| +---------+--------+---------+ |querybc | 800 | 0xd...|-->从表中获取 |queryab | 600 | 0x2….|-->从表中获取 |queryab | 592 | 0x7..|-->从表中获取 |queryab | 550 | 0x4..|-->从表中获取 |queryaa | 500 | 0x1…|-->从表中获取 +---------+--------+---------+

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

    :“查询每个表索引,除非优化器认为使用表扫描更有效,否则将使用最佳索引。曾经,根据最佳索引是否跨越表的30%以上使用扫描,但固定百分比不再决定使用索引还是扫描。优化器现在更加复杂,它的估计基于其他因素,如表大小、行数和I/O块大小。“谢谢eggyal,所以是0,3,信息有点过时。无论如何,我认为我的观点仍然有效。同意,我只是补充说明。)@tombom您会推荐什么方法?如果没有关于您的模式、用例等的详细信息,很难说清楚。也许查找表是一个带有散列索引的选项。最简单的方法当然是用适当的复合索引尽可能多地过滤查询,但是所有这些都是要问和考虑的,你最好问一个不同的问题。所以,实际上你需要找到匹配查询的所有单词。像“john%”这样的按重量排序的查询对他来说真的很难吗?@Noam:我是说这取决于索引前缀的长度。 +---------+--------+---------+ | 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 +---------+--------+---------+