【问题标题】:Improving filepath search in mysql改进mysql中的文件路径搜索
【发布时间】:2014-04-30 22:07:29
【问题描述】:

我需要搜索数百万个文件名。它们看起来像这样:

LG_MARGINCALL_HD2CH_127879834_EN.mov

如果有人搜索以下任何内容,它应该匹配:

  • 边距
  • 追加保证金
  • 追加保证金 mov
  • 追加保证金高清
  • 追加保证金高清动画

我目前使用的是 mysql %LIKE% 搜索。比如:

SELECT filename FROM path WHERE filename LIKE '%margin%' AND filename LIKE '%mov%'

速度非常慢(搜索最多可能需要十秒钟)。请注意,它确实有效

进行上述搜索的更好方法是什么?使用 mysql 或其他程序。

【问题讨论】:

  • 用户会按文件名、片名还是两者都搜索?
  • 他们将同时搜索。
  • 我建议看看 Lucene,它非常适合这种搜索。
  • 听起来像是一个新的 bittorrent 服务 :)

标签: mysql sql unix search full-text-search


【解决方案1】:

如您所见,您的搜索策略很慢。很慢,因为

 LIKE '%something%'

必须扫描表以查找匹配项。 LIKE 搜索中的前导 % 符号是破坏性能的绝佳方式。

我不知道您的path 表中有多少列。如果有很多列,您可以做两件快速的事情来提高性能:

  1. 去掉SELECT * 并在结果集中列出您想要的列的名称。
  2. 创建一个复合索引,其中包含您的 filename 列和您需要检索的其他列。

(如果您的表中只有几列,这将无济于事。)

您不能使用直接出的软件包FULLTEXT 搜索这些东西,因为它是为语言文本设计的。

如果我必须让这项工作快速投入生产,我会这样做:

首先,创建一个名为“searchterm”的新表,其中包含

 filename_id INT   the id number of a row in your path table
 searchterm  VARCHAR(20)  a fragment of a filename.

其次,编写一个读取filename_idfilename 值的程序,并为每个值插入一堆不同的行到searchterm。对于您显示的项目,值应该是:

LG_MARGINCALL_HD2CH_127879834_EN.mov   (original)
LG  MARGINCALL  HD2CH  127879834  EN  mov   (split on punctuation)
 HD 2 CH                                    (split on embedded numerics)
 MARGIN CALL                                (split on an app-specific list of words)

因此,您的搜索词表中有一堆条目,它们都具有相同的 filename_id 值和许多不同的小块文本。

最后,在搜索时你可以这样做。

 SELECT path.id, path.filename, path.whatever,
        COUNT(DISTINCT searchterms.term) AS termcount
   FROM path
   JOIN searchterm ON path.filenanme_id = search.filename_id
  WHERE searchterm.term  IN ('margin','call','hd','en', 'mov')
  GROUP BY path.id, path.filename, path.whatever
  ORDER BY path.filename, COUNT(DISTINCT searchterms.term) DESC

这个小查询会找到与您要搜索的内容匹配的所有片段。它返回多个文件名,并按照匹配最多术语的顺序显示它们。

我的建议是您创建自己的特定于应用程序的有点像全文搜索系统。如果你真的有几百万个多媒体文件,这肯定是值得你努力的。

【讨论】:

  • 感谢您的回答。我喜欢上面的想法,但是在弄清楚我们将如何做诸如“特定于应用程序”的关键字拆分之类的事情时遇到了麻烦,例如电影标题:追加保证金->追加保证金。我们并不真正知道基于文件的所有电影名称(通常可能会缩写)。有没有办法单独在标题上做类似 %LIKE% 搜索的事情?
  • 在任何 Linux 或 *nix 发行版中都有一个免费的(像小猫一样免费,像语音一样免费)文件,里面装满了常用词。见en.wikipedia.org/wiki/Words_(Unix)。您也许可以使用该文件以有用的方式帮助分解诸如 MARGINCALL 之类的短语。
  • 谢谢,这很有帮助,我认为这对于英文标题来说效果很好。外国头衔呢?例如:ABRESUSOJOS_HD.mov (en.wikipedia.org/wiki/Open_Your_Eyes_(1997_film))。
  • 一些可能的来源:freedict.org/en ftp.gnu.org/gnu/aspell/dict 我认为这些会比英文单词列表更难使用。
  • @user1436531,对于外文标题来说,拆分成单词很难,而且变音符号(é,ü... -> e,ue...)和连字(æ,ß.. ., ae, ss...),它可能是“规范化为英语”,也可能不是
【解决方案2】:

您显然需要全文搜索功能。

有多种解决方案可以对此做出回应,目前最好的解决方案之一是 Elastic Search

它具有处理实时全文搜索的所有功能。 通过提供自动建议、自动完成等功能,它在很大程度上不仅限于此。

而且它是开源的。

【讨论】:

    【解决方案3】:

    停止使用like 语句,改为使用match() 并为您的搜索列使用全文索引,并且您的表必须是MYISAM 表(我不知道是不是)

    【讨论】:

      【解决方案4】:

      我建议尝试 2 件事以获得更好的性能。第一个是在select前面使用关键字EXPLAIN。这可能会给您一些关于查询性能缓慢的帮助。但我认为这不会有太大帮助。第二件事是使用REGEXP。所有这些的一个例子:

      EXPLAIN SELECT filename FROM path WHERE filename LIKE REGEXP '^.*MAR{1}.*mov{1}'
      

      但是你必须多搜索一点来优化正则表达式。

      【讨论】:

        【解决方案5】:

        尝试使用 SPHINX 进行全文搜索。 http://sphinxsearch.com/

        【讨论】:

          【解决方案6】:

          这可能比使用AND 更快:

          SELECT filename FROM path WHERE filename LIKE '%margin%call%hd%en%mov%'
          

          但是在字符串的开头有一个“%”总是会让它变慢。

          您应该在字段上使用全文搜索索引,然后使用类似:

          SELECT filename FROM path WHERE MATCH(filename) AGAINST('+margin +call +hd +en +mov' IN BOOLEAN MODE);
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-08-08
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多