针对 REGEXP 的 Mysql 优化

Posted

技术标签:

【中文标题】针对 REGEXP 的 Mysql 优化【英文标题】:Mysql optimization for REGEXP 【发布时间】:2012-09-01 08:03:51 【问题描述】:

这个查询(用不同的名字代替“jack”)在我的慢查询日志中发生了很多次。为什么?

Users 表有很多字段(超过我选择的这三个)和大约 40.000 行。

select name,username,id from Users where ( name REGEXP '[[:<:]]jack[[:>:]]' ) or ( username REGEXP '[[:<:]]jack[[:>:]]' ) order by name limit 0,5;

id 是主要且自动递增的。name 具有索引。username 具有唯一索引。

有时需要 3 秒! 如果我解释 mysql 上的选择,我有这个:

select type: SIMPLE
table: Users
type: index
possible keys: NULL
key: name
key len: 452
ref: NULL
rows: 5
extra: Using where

这是我能做到的最好的吗?我能解决什么问题?

【问题讨论】:

试试(name like '%jack%' and name REGEXP '[[:&lt;:]]jack[[:&gt;:]]' ) or ( username like '%jack%' and username REGEXP '[[:&lt;:]]jack[[:&gt;:]]' )。如果您需要更好的性能,请使用 FULLTEXT 索引或一些第三方工具。 正如@ImreL 建议的那样,如果您在 REGEXP 之前添加一个 LIKE %...% 子句,它本质上充当过滤器,以减少潜在结果的数量,然后再尝试 REGEXP。我发现这可以将 2 秒的慢查询缩短到 0.3 秒。 【参考方案1】:

如果你必须使用正则表达式WHERE 子句,你肯定会被查询慢的问题所困扰。为了使 regexp 样式的搜索起作用,MySQL 必须将 name 列中的每个值与 regexp 进行比较。而且,您的查询还通过查看您的用户名列使麻烦加倍。

这意味着 MySQL 无法利用任何索引,这就是所有 DBMS 加快大表查询速度的方式。

您可以尝试一些事情。所有这些都涉及到告别 REGEXP。

一个是这样的:

WHERE name LIKE CONCAT('jack', '%') OR username LIKE CONCAT('jack', '%')

如果您在名称和用户名列上创建索引,这应该很快。它将查找以“jack”开头的所有名称/用户名。注意

WHERE name LIKE CONCAT('%','jack') /* SLOW!!! */

将查找以“jack”结尾的名称,但会像您的正则表达式样式搜索一样慢。

您可以做的另一件事是弄清楚为什么您的应用程序需要能够搜索名称或用户名的一部分。您可以从您的应用程序中消除此功能,或者找出一些更好的方法来处理它。

可能的更好方法:

    要求您的用户将他们的姓名分成名字和姓氏字段,并分别进行搜索。 创建一个单独的“搜索所有用户”功能,该功能仅在用户需要时使用,从而降低慢速正则表达式样式查询的频率。 自己使用某种预处理程序将他们的名字分解成一个单独的名字词表。搜索不使用正则表达式的名称词表。 了解如何使用 MySQL 全文搜索来实现此功能。

所有这些都涉及到一些编程工作。

【讨论】:

在我的查询 REGEXP '[[:<:>:]]' 意味着我想找到像“Jack”或“The Jack Rock”这样的记录,而不是“Jacky” ",因为我正在寻找完全匹配的单词。所以,使用 LIKE 'jack%' 会不好。由于找不到“杰克摇滚”。如果我使用 '%jack%' 会不会像 '%jack' 一样慢? 明白。正则表达式搜索是您问题的正确解决方案。但它本质上很慢。对未锚定的文本列索引的任何搜索(即,不从列中的第一个字符位置开始)都有相同的问题。所以,是的,LIKE '%jack%' 像正则表达式搜索一样慢。这是因为索引是有组织的,并且可以按顺序快速随机访问。想想在电话簿中查找“琼斯”。您可以轻松找到具有该名称的第一个人。但是,如果你用“一”字来查找每个人,这需要很长时间。 使用 FULLTEXT 索引更好? 是的,使用全文搜索应该会有所帮助。我想你会想要使用布尔模式。 谢谢 Ollie,我会添加 FULLTEXT 索引。【参考方案2】:

仅通过在 where 子句中添加 fieldname != '' 就可以实现 50% 的加速。它使mysql能够使用索引。

SELECT name, username, id 
FROM users 
WHERE name != '' 
    AND (name REGEXP '[[:<:]]jack[[:>:]]' or username REGEXP '[[:<:]]jack[[:>:]]') 
ORDER BY name 
LIMIT 0,5;

不是一个完美的解决方案,但有帮助。

【讨论】:

这在很大程度上取决于表/数据结构。【参考方案3】:

在前面加上“喜欢”

来自

SELECT cat_ID, categoryName FROM category WHERE cat_ID REGEXP '^15-64-8$' ORDER BY categoryName

SELECT cat_ID, categoryName FROM category WHERE cat_ID LIKE '15-64-8%' and cat_ID REGEXP '^15-64-8$' ORDER BY categoryName

对于 cos,只有当 U r 搜索短语 U 知道以什么开头时才有效,否则全文索引是解决方案。

【讨论】:

以上是关于针对 REGEXP 的 Mysql 优化的主要内容,如果未能解决你的问题,请参考以下文章

针对跑MySQL的Linux优化

MYSQL 中针对子查询的查询优化? [关闭]

mysql之 CentOS系统针对mysql参数优化

针对标签上的多对多连接优化 MySQL 查询

针对 PHP 中的数据检索优化的 MySQL 数据库设计

MySQL 原理与优化:Update 优化