【问题标题】:MySQL numberplate relevence search performance?MySQL车牌相关性搜索性能?
【发布时间】:2012-01-24 10:40:15
【问题描述】:

我遇到了一个小问题,非常感谢您的帮助。

我有一个包含 33,000,000 行的 MyISAM 表,其数据结构如下:

id -> Primary Key, Unsigned INT, Auto-Increment
characters -> Unique Indexed, varchar(15)
price -> decimal (10,2)
active -> tinyint(1)

我编写了一个脚本,该脚本从用户那里获取搜索值,然后根据用户输入“Kevin”创建以下查询:

SELECT characters, price 
FROM listings_dvla 
WHERE active=TRUE 
AND LOWER(REPLACE(characters, ' ', '')) REGEXP '^[a-z0-9]*[(k)]+[a-z0-9]?[(e)(3)]+[a-z0-9]?[(v)]+[a-z0-9]?[(i)(1)]+[a-z0-9]?[(n)(11)(1v)]+[a-z0-9]*' 
ORDER BY characters ASC 
LIMIT 0, 12

只是为了解释一下,正则表达式只是试图按顺序匹配每个字母,或者在“车牌语言”中表示相同的字母序列,例如。 N == 1V

问题是,这个查询需要该死的时间! 20+秒。我已经阅读了相当多的内容,发现将字符作为主键比较慢,所以我把它拿出来并添加了一个 ID 字段。我使用了 EXPLAIN 来显示索引并且它们为 NULL,我认为这是因为 REGEXP 禁用了它们(我也在某处读过)。

我的问题是,有没有人有任何好主意来大幅提高查询性能?因为我觉得我没有意识到让这个速度更快的非常关键的事情。

如果需要,我会更改表结构,所以不要担心给我一个相当极端的答案。

感谢阅读本文,如有任何建议,将不胜感激。

【问题讨论】:

  • 如果您删除字符列上的 LOWER 和 REPLACE ,时间是否会有所不同?通常,您希望避免对 where 子句中的列执行字符串操作,因为不太可能使用索引。还可以使用直接的 `characters = 'kevin' 或完全匹配的东西进行测试。您最好在代码中调整用户输入,而不是运行多个查询而不是一个执行正则表达式匹配并操作您搜索的列的查询。

标签: mysql performance search full-text-search


【解决方案1】:

mysql 中的字符串查询非常慢。我什至不确定是否有任何键(FULLTEXT 除外)。

您在 where 条件下使用函数,这意味着必须加载 每条记录,用 replace 和 lower 进行更新,而不是与 regexp 进行比较(mysql 无法提前知道结果是什么会的)。

基本上:使用这种正则表达式,您将永远无法快速完成此查询。

但是您可以添加诸如isWord TINYINT DEFAULT 0 之类的字段(带有索引)并使用查询:

UPDATE listings_dvla SET isWord = 1 WHERE active=TRUE 
AND LOWER(REPLACE(characters, ' ', '')) REGEXP '^[a-z0-9]*[(k)]+[a-z0-9]?[(e)(3)]+[a-z0-9]?[(v)]+[a-z0-9]?[(i)(1)]+[a-z0-9]?[(n)(11)(1v)]+[a-z0-9]*' 

然后通过 *索引字段 选择记录:SELECT ... WHERE ... AND isWord = 1

【讨论】:

  • 虽然更新查询需要先运行,所以这与正常搜索它所花费的时间一样长吗?还是我在这里误会了你?
  • @KevinOrriss 是的,首先需要 20 多秒,但我无法想象它工作得更快
【解决方案2】:

由于您没有在 where 子句中使用 ID 字段,因此 mysql 没有使用您的主键。那里没有惊喜。

我猜,您需要的是字符列上的全文索引。

【讨论】:

    【解决方案3】:

    首先,您可以“清理”字符字段,这样您就不必将其转换为低位并去除空格。此操作几乎可以肯定意味着您错过了任何索引的好处。

    其次,显而易见的替代方法是在客户端运行将“kevin”修改为各种车牌形式的代码,并将其转换为“in”查询:

    select *
    from listings_dvla 
    where active = 1
    and cleaned_characters in ('kev1n', 'kev1iv'.....)
    

    如果您还希望能够在字符中搜索单词 - 即为参数 KEV 返回 A10 KEV,您可以通过创建带有子字符串的附加列来作弊。

    表格列表_dvla

    ID    characters   cleaned_characters   characters_right7  characters_right6 characters_right5 characters_right4 characters_right3
    1     A10 KEV       a10kev               10kev               0kev             kev     
    2     KT 11 TCP     kt11tcp              t11tcp               11tcp           1tcp     tcp    
    

    很脏,但是通过在所有列上创建索引,您应该能够获得非常快速的查询。不过,插入/更新会更慢...

    【讨论】:

    • 假设我搜索了“kev”,您的查询将无法找到车牌“A10 KEV”。精确搜索似乎很快,直到我尝试使用 LIKE 或 REGEXP 才会减慢速度。
    • 啊,对 - 没有足够详细地查看 reg exp 来了解它。 LIKE 和 REGEXP 几乎肯定不会使用索引;已修改答案以显示如何解决此问题。
    猜你喜欢
    • 1970-01-01
    • 2019-07-31
    • 2011-01-26
    • 2014-08-29
    • 2014-02-18
    • 2012-10-30
    • 2011-06-29
    • 1970-01-01
    相关资源
    最近更新 更多