我的问题是关于在使用前缀匹配时在VARCHAR上处理MySQL索引以及使用int COLUMN。 例如如果我有这样的疑问:
SELECT * FROM tbl WHERE name LIKE 'query%' ORDER BY weight DESC LIMIT 5
考虑到我有一个索引一个名称 - >重量,该索引是否需要找到前缀query
的所有外观然后是ORDER BY,或者即使使用前缀也保持交叉计算的索引匹配(%)。我很困扰它,因为对于流行的名字(例如查询=约翰),我可能会发现自己长时间搜索约翰的所有外观,这将使限制无用,并且查询变得缓慢,因为我正在处理使用大型数据集。
答案 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
的索引前缀:
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.... | : : : :
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.... | : : : :
此时,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.... | : : : :
只有这样才能使用前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 +---------+--------+---------+
如果name
更长而不是{{1}}的索引前缀,那么MySQL必须在上面的步骤1中对表进行查找,以便充分过滤记录随后下令。