搜索具有相关性的大型mysql数据库

时间:2013-01-28 11:41:06

标签: php mysql search left-join inner-join

我正在为我们公司的内联网构建一个相当大的“搜索”引擎,它有1个以上的条目 它运行在速度相当快的服务器上,但一些搜索查询需要1分钟。

这是表格的样子

Table

我尝试为它创建一个索引,但好像我错过了一些东西,这就是show index的显示方式

idexes

这是查询本身,它的排序大大减慢了查询,但即使是没有排序的查询也有点慢。

SELECT SQL_CALC_FOUND_ROWS *
FROM `businessunit`
INNER JOIN `businessunit-postaddress` ON `businessunit`.`Id` = `businessunit-postaddress`.`BusinessUnit`
WHERE `businessunit`.`Name` LIKE 'tanto%'
ORDER BY `businessunit`.`Premium` DESC ,
CASE WHEN `businessunit`.`Name` = 'tanto'
THEN 0
WHEN `businessunit`.`Name` LIKE 'tanto %'
THEN 1
WHEN `businessunit`.`Name` LIKE 'tanto%'
THEN 2
ELSE 3
END , `businessunit`.`Name`
LIMIT 0 , 30

非常感谢任何帮助

修改 什么令这个查询窒息99%是与野生字符%相关的排序 当我做一个解释它说使用在哪里;使用fsort

10 个答案:

答案 0 :(得分:18)

您应该尝试使用sphinx搜索解决方案,这是一个全文搜索引擎将为您提供非常好的性能以及许多设置相关性的选项。

Click here了解详情。

答案 1 :(得分:6)

似乎索引不包含Premium,但这是第一个ORDER BY参数。

使用EXPLAIN your query here找出查询计划并更改索引以删除任何表扫描,如http://dev.mysql.com/doc/refman/5.0/en/using-explain.html

中所述

答案 2 :(得分:6)

MySQL适用于存储数据,但在快速基于文本的搜索时效果不佳。

除了已经建议过的Sphinx,我推荐两款出色的搜索引擎:

  1. Solrhttp://pecl.php.net/package/solr - 非常受欢迎的搜索引擎。用于NetFlix等大型服务。

  2. Elastic Search - 相对较新的软件,但社区非常活跃且受到很多尊重

  3. 两种解决方案都基于相同的库Apache Lucene

答案 3 :(得分:2)

大多数搜索引擎定向网站均使用FULL-TEXT-SEARCH。     与selectLIKE相比,它会更快...     我添加了一个示例和一些链接......     我认为这对你有用......     在这个全文搜索中也有一些条件...

<强> STEP:1

CREATE TABLE articles (
    id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
    title VARCHAR(200),
    body TEXT,
    FULLTEXT (title,body)
);

<强> STEP:2

INSERT INTO articles (title,body) VALUES
    ('MySQL Tutorial','DBMS stands for DataBase ...'),
    ('How To Use MySQL Well','After you went through a ...'),
    ('Optimizing MySQL','In this tutorial we will show ...'),
    ('1001 MySQL Tricks','1. Never run mysqld as root. 2. ...'),
    ('MySQL vs. YourSQL','In the following database comparison ...'),
    ('MySQL Security','When configured properly, MySQL ...');

STEP:3
自然语言全文搜索:

SELECT * FROM articles
    WHERE MATCH (title,body) AGAINST ('database');

布尔全文搜索

SELECT * FROM articles WHERE MATCH (title,body)
     AGAINST ('+MySQL -YourSQL' IN BOOLEAN MODE);

浏览此链接 viralpatel.netdevzone.zend.comsqlmag.comcolorado.eduen.wikipedia.org

答案 4 :(得分:2)

如果“ORDER BY”确实是瓶颈,那么直接的解决方案是从查询中删除“ORDER BY”逻辑,并使用C#排序直接在应用程序代码中重新实现排序。不幸的是,这意味着您还必须将您的分页移动到您的应用程序中,因为您需要获得完整的结果集才能进行排序和排序。分页吧。我只是提到这一点,因为到目前为止还没有人想到它。

坦率地说(就像其他人指出的那样),您在顶部显示的查询不应该需要全文索引。只要在相关列上有BTREE(而不是HASH)索引,单个后缀通配符(例如,LIKE'ABC%')应该非常有效。

而且,就个人而言,我甚至不厌恶双通配符(例如,LIKE'%ABC%),这当然永远不会使用索引,只要全表扫描便宜。可能250,000行我将开始认真考虑全文索引.10,000绝对没问题。

我总是确保我的SELECT是脏读(但没有应用于选择的事务)。

在任何情况下,一旦它到达用户的眼球就会变脏!

答案 5 :(得分:1)

这是如此奇怪的查询:) 让我们试着了解它的作用。

在某些条件下,“businessunit”表中的结果少于30行。

第一个条件是“businessunit-postaddress”表的外键 请检查列businessunit-postaddress上是否有索引。BusinessUnit

第二个是仅使用businessunit返回行的过滤器。Name以'tanto'开头。
如果我没有犯错,你有一个非常复杂的索引'商业'由11个字段组成!
字段“名称”不是此索引中的第一个字段 所以当你运行“像tanto%”的查询时,这个索引是没用的 我完全怀疑这个指数的必要性。
顺便说一句,它需要相当大的资源来维护和减慢编辑操作 您必须使用唯一字段“名称”创建索引。

过滤后,查询会对结果进行排序,并以一种奇怪的方式进行排序 首先按字段businessunit排序。Premium - 这是正常的 然而,CASE的下一个陈述也没用 这就是原因。 零被分配给Name ='tanto'(确切地说) 带有一个的下一行是'tanto'后面有空格的行 - 这些行在任何情况下都会在'tanto'之后(特殊符号除外)导致空间低于任何字母。
带有两个的下一行是'tanto'之后带有一些字母的行(包括空格!)。根据定义,这些行也将按此顺序排列 这三个是“保留”为“其他”行,但你不会得到“其他”行 - 记住[WHERE businessunitName LIKE'tanto%']条件。
因此,ORDER BY的这一部分毫无意义 在ORDER BY结束时,再次businessunitName ...

我的建议:您需要从头开始重建查询,记住您想要获得的内容。

无论如何我想你可以使用

SELECT SQL_CALC_FOUND_ROWS *
FROM `businessunit`
INNER JOIN `businessunit-postaddress` ON `businessunit`.`Id` = `businessunit-postaddress`.`BusinessUnit`
WHERE `businessunit`.`Name` LIKE 'tanto%'
ORDER BY `businessunit`.`Premium` DESC,
`businessunit`.`Name`
LIMIT 0 , 30

不要忘记字段businessunit-postaddress上的索引。BusinessUnit

我对Field Premium有很强的假设。 我想它是专为存储二进制数据而设计的(是/否)。 所以普通(BTREE)索引不匹配。 你必须使用位图索引。

P.S。我不确定你真的需要使用SQL_CALC_FOUND_ROWS MySQL: Pagination - SQL_CALC_FOUND_ROWS vs COUNT()-Query

答案 6 :(得分:1)

它是全文(http://dev.mysql.com/doc/refman/5.0/en/fulltext-search.html)或来自php和mysql端的模式匹配(http://dev.mysql.com/doc/refman/5.0/en/pattern-matching.html)。

从经验和理论:

全文的优点 -
1)结果非常相关,并且在搜索查询中限制间距等字符不会妨碍搜索 全文的缺点 -
1)有一些停用词被网站主人用来限制,以防止数据过载。(例如,不显示包含单词'one'或'moz'的搜索结果。如果您正在运行自己的服务器,这可以避免保持没有停止词 2)如果我输入'ree',它只显示包含'ree'而不是'three'或'reed'的单词。

模式匹配的优点 -
1)它没有全文中的任何停用词,如果你搜索'ree',它会显示任何包含'ree'的单词,如'reed'或'three',而不像全文,其中只有确切的单词被检索。
模式匹配的缺点 -
1)如果在搜索词中使用了像空格这样的分隔符,并且结果中没有这些空格,因为每个单词都与任何分隔符分开,那么它不会返回任何结果。

答案 7 :(得分:0)

如果LIKE的参数不以wildchard字符开头,就像你的例子一样,LIKE运算符应该能够take advantage of indexes

在这种情况下,LIKE运算符应该比LOCATE或LEFT表现更好,所以我怀疑改变这样的条件会让事情变得更糟,但我仍然认为值得尝试(谁知道?):

WHERE LOCATE('tanto', `businessunit`.`Name`)=1

或:

WHERE LEFT(`businessunit`.`Name`,5)='tanto'

我还会按条款更改您的订单:

ORDER BY
  `businessunit`.`Premium` DESC ,
   CASE WHEN `businessunit`.`Name` LIKE 'tanto %' THEN 1
        WHEN `businessunit`.`Name` = 'tanto'      THEN 0
        ELSE 2 END,
   `businessunit`.`Name`

名称必须是LIKE'tanto%',因此您可以跳过条件(CASE永远不会返回值3)。当然,请确保将Premium字段编入索引。

希望这有帮助。

答案 8 :(得分:0)

我认为你只需要收集密钥,对它们进行排序,然后再加入

SELECT A.*,B.* FROM
(
    SELECT * FROM (
        SELECT id BusinessUnit,Premium
            CASE
                WHEN Name = 'tanto'      THEN 0
                WHEN Name LIKE 'tanto %' THEN 1
                WHEN Name LIKE 'tanto%'  THEN 2
                ELSE 3
            END SortOrder
        FROM businessunit Name LIKE 'tanto%'
    ) AA ORDER BY Premium,SortOrder LIMIT 0,30
) A LEFT JOIN `businessunit-postaddress` B USING (BusinessUnit);

这仍然会生成一个文件分区。

您可能需要考虑在可以索引的单独表格中预加载所需的密钥。

CREATE TABLE BusinessKeys
(
    id int not null auto_increment,
    BusinessUnit int not null,
    Premium      int not null,
    SortOrder    int not null,
    PRIMARY KEY (id),
    KEY OrderIndex (Premuim,SortOrder,BusinessUnit)
);

填充所有匹配的键

INSERT INTO BusinessKeys (BusinessUnit,Premuim,SortOrder)
SELECT id,Premium
    CASE
        WHEN Name = 'tanto'      THEN 0
        WHEN Name LIKE 'tanto %' THEN 1
        WHEN Name LIKE 'tanto%'  THEN 2
        ELSE 3
    END
FROM businessunit Name LIKE 'tanto%';

然后,要进行分页,只在BusinessKeys上运行LIMIT

SELECT A.*,B.*
FROM
    (
        SELECT FROM BusinessKeys
        ORDER BY Premium,SortOrder
        LIMIT 0,30
    ) BK
    LEFT JOIN businessunit A ON BK.BusinessUnit = A.id
    LEFT JOIN `businessunit-postaddress` B ON A.BusinessUnit = B.BusinessUnit
;

CAVEAT :我使用LEFT JOIN代替INNER JOIN,因为LEFT JOIN会保留查询左侧的键的顺序。

答案 9 :(得分:0)

我已经阅读了使用Sphinx优化搜索的答案。但根据我的经验,我会建议一个不同的解决方案。我们使用Sphinx已经有好几年了,并且有一些令人讨厌的问题,包括分段错误和损坏的indice。也许狮身人面像几年前并没有像马车一样,但是现在我们对一个不同的解决方案非常满意:

http://www.elasticsearch.org/

好处:

  • 可扩展性 - 您可以简单地添加另一台配置几乎为零的服务器。如果你知道mysql复制,你会喜欢这个功能
  • 速度 - 即使在重负荷下,您也可以在不到一秒的时间内获得良好的效果
  • 易于学习 - 只有了解HTTP和JSON才能使用它。如果您是Web开发人员,那么您会感觉像家一样
  • 易于安装 - 无需触摸配置即可使用。您只需要简单的Java(没有Tomcat或其他)和防火墙来阻止来自公众的直接访问
  • 良好的Javascript集成 - 即使像phpMyAdmin一样的工具也是使用Javascript的简单HTML页面:https://github.com/mobz/elasticsearch-head
  • https://github.com/ruflin/Elastica
  • 良好的PHP集成
  • 良好的社区支持
  • 良好的文档(它不是眼睛友好,但几乎涵盖了所有功能!)

如果您需要其他存储解决方案,则可以轻松地将搜索引擎与http://couchdb.apache.org/

结合使用