优化 MySQL 全文搜索查询?

Posted

技术标签:

【中文标题】优化 MySQL 全文搜索查询?【英文标题】:Optimising the MySQL full text search Query? 【发布时间】:2013-06-24 14:32:57 【问题描述】:

我正在使用 mysql 作为数据库在 php 中为网站创建产品搜索系统。

目前我们有超过 10 万种产品可供搜索。我曾考虑使用 MySQL 的内置全文功能(Match and Against),而不是 Sphinx 或 Lucene,因为我们的要求强度较低。​​

所以目前我们使用 MySQL 内置的全文搜索功能,有人可以帮助我们优化这个全文搜索查询,以提高效率并减少开销。

正如有人在 SO 上向我建议的那样,我可能没有以正确的方式使用全文语法。

创建表结构

-- Create Table SQL for Product --
DROP TABLE IF EXISTS `ps_search__p`;
CREATE TABLE IF NOT EXISTS `ps_search__p` (
  `id` int(10) unsigned NOT NULL,
  `code` varchar(100) NOT NULL,
  `type` varchar(100) DEFAULT NULL,
  `name` text,
  `keywords` text,
  `material` text,
  `material_def_id` int(11) unsigned DEFAULT NULL,
  `s_type` text,
  `s_fabric` text,
  `price` decimal(20,6) unsigned DEFAULT NULL,
  `build_type` varchar(12) NOT NULL,
  `genre` varchar(12) NOT NULL DEFAULT 'p',
  `categories` text,
  PRIMARY KEY (`id`),
  FULLTEXT KEY `code` (`code`,`type`,`name`,`keywords`,`material`,`s_type`,`s_fabric`,`categories`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;


-- Create SQL Table for S table --
DROP TABLE IF EXISTS `ps_search__s`;
CREATE TABLE IF NOT EXISTS `ps_search__s` (
  `id` int(20) unsigned NOT NULL,
  `s_id` varchar(100) NOT NULL,
  `type` varchar(100) NOT NULL,
  `fabric` varchar(100) DEFAULT NULL,
  `name` varchar(100) DEFAULT NULL,
  `price` decimal(20,6) unsigned DEFAULT NULL,
  `genre` varchar(12) NOT NULL DEFAULT 's',
  `categories` text,
  PRIMARY KEY (`id`),
  FULLTEXT KEY `s_id` (`s_id`,`type`,`fabric`,`name`,`categories`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

全文搜索查询

SELECT SQL_CALC_FOUND_ROWS * FROM (
    ( SELECT 
        `id`, `code`, `name`, `material`,`material_def_id`, `s_type`, `s_fabric`, `price`, `genre`, `categories`,
        MATCH (`code`,`type`,`name`,`keywords`,`material`,`s_type`,`s_fabric`,`categories`)
        AGAINST ('+cotton*' IN BOOLEAN MODE) AS `relevance`
    FROM `ps_search__p`
    WHERE
        ( MATCH (`code`,`type`,`name`,`keywords`,`material`,`s_type`,`s_fabric`,`categories`)
        AGAINST ('+cotton*' IN BOOLEAN MODE))
        AND `s_type` REGEXP '.*'
        AND `s_fabric` REGEXP '.*'
        AND `material` REGEXP '.*'
        AND `price` REGEXP '.*'
        AND `categories` REGEXP '.*'
        )
UNION ALL
    ( SELECT
        `id`, `s_id`, `name`, NULL AS `material`, NULL AS `material_def_id`, `type`, `fabric`, `price`, `genre`, `categories`,
        MATCH (`s_id`,`type`,`fabric`,`name`,`categories`)
        AGAINST ('+cotton*' IN BOOLEAN MODE) AS `relevance`
    FROM `ps_search__s`
    WHERE
        ( MATCH (`s_id`,`type`,`fabric`,`name`,`categories`)
        AGAINST ('+cotton*' IN BOOLEAN MODE))
        AND `type` REGEXP '.*'
        AND `fabric` REGEXP '.*'
        AND `price` REGEXP '.*'
        AND IFNULL(`categories`, '') REGEXP '.*' )
) AS `tblsearch`
ORDER BY `relevance` DESC
LIMIT 0, 36

并获取查询计数

SELECT FOUND_ROWS() 'recordsnum';

即使是一点点帮助也将不胜感激。

【问题讨论】:

没有办法让这个查询成为任何因为优化,使用狮身人面像,将需要不到 2 天,而且更快!!! 这些搜索条件在您的应用程序中的用途是什么? AND s_type REGEXP '.*' 保证他们不会使用任何索引。 天哪,是的,请尽快将REGEXP '.*' 替换为IS NOT NULL!或者更好的是,在这些列上添加 NOT NULL 约束并完全摆脱这个测试。 @Yak, [Ollie] - 我表中的某些列的数据以逗号分隔格式存储,例如“棉、亚麻、尼龙”。因此,要在这些列中搜索特定或多个单词,我更喜欢使用REGEXP 'Cotton|Nylon',这无法通过LIKE 运算符实现。如果搜索过滤器中没有关键字,我使用REGEXP '.*',这意味着匹配所有。 不过会尝试你的建议,让你们知道我的成功,感谢@OllieJones 和 [at]YaK 的帮助,感谢 【参考方案1】:

将所有测试 REGEXP '.*' 替换为 IS NOT NULL

利用全文索引,将fabric REGEXP 'Cotton|Nylon' 等测试替换为MATCH(fabric) AGAINST ("+Cotton" IN BOOLEAN MODE) OR MATCH(fabric) AGAINST ("+Nylon" IN BOOLEAN MODE)

Normalize your database。列不应包含非标量值(例如 CSV 数据)。相反,应该与新表建立一对多关系。

【讨论】:

看了你最初的评论后肯定有同样的想法,明天试试,让你知道

以上是关于优化 MySQL 全文搜索查询?的主要内容,如果未能解决你的问题,请参考以下文章

MYSQL模糊查询优化(使用全文索引进行左右模糊查询) match() against ()的简单使用以及介绍

如何优化具有多个结果的 MySQL/MyISAM 全文搜索

MySQL数据检索+查询+全文本搜索

怎么MySql添加全文索引

云服务器上Postgres全文搜索的进一步优化

3.高并发教程-基础篇-之分布式全文搜索引擎elasticsearch的搭建