如何快速搜索数据库中非常大的字符串/记录列表


32

我有以下问题:我的数据库包含超过200万条记录。每个记录都有一个字符串字段X,我想显示一个记录列表,其中字段X包含某个字符串。每条记录的大小约为500字节。

更具体地说:在我的应用程序的GUI中,我有一个文本字段,可以在其中输入字符串。在文本字段上方,我有一个表,显示与文本字段中的字符串匹配的(前N个,例如100个)记录。当我在文本字段中键入或删除一个字符时,表内容必须即时更新。

我想知道是否存在使用适当的索引结构和/或缓存的有效方法。如上所述,我只想显示与查询匹配的前N个项目。因此,对于足够小的N来说,从数据库中加载匹配项应该不是大问题。此外,在主存储器中缓存项目可以使检索更快。

我认为主要问题是在给定模式字符串的情况下如何快速找到匹配项。我可以依靠某些DBMS工具,还是必须自己构建一些内存索引?有任何想法吗?

编辑

我已经进行了第一次实验。我已将记录分为不同的文本文件(每个文件最多200条记录),并将文件放在不同的目录中(我使用一个数据字段的内容来确定目录树)。我最终得到大约40000个目录中的大约50000个文件。然后,我运行Lucene来索引文件。使用Lucene演示程序搜索字符串非常快。拆分和建立索引花费了几分钟:对于我来说这完全可以接受,因为它是我要查询的静态数据集。

下一步是将Lucene集成到主程序中,并使用Lucene返回的命中将相关记录加载到主存储器中。


2
200万条记录* 500字节= 1 GB数据。这是一个很大的数据进行搜索,你去了解它无论怎样-是X的每个值可能是唯一的,否则你将与X相同的值多条记录?

1
尝试将其作为高速缓存存储在内存中的数据也很多。这相当于每个用户会话超过1GB。
maple_shaft

我之前的评论假定使用Web应用程序。这是网络应用程序吗?
maple_shaft

它是一个桌面应用程序。记录中的值不一定是唯一的。另外,我正在搜索子串而不是完全匹配。
乔治

@maple_shaft:我只会缓存最近访问的记录。如果更改查询字符串,但记录仍然匹配,则它仍在高速缓存中。
乔治

Answers:


20

无需将数据放在数据库中,您可以将它们分别作为一组文档(文本文件)保存,并将链接(路径/ URL等)保存在数据库中。

这是必不可少的,因为在设计子查询和检索时,SQL查询的设计都会非常慢。

现在,您的问题表达为,必须搜索包含字符串集的文本文件。这里有两种可能性。

  1. 子字符串匹配如果您的文本斑点是单个字符串或单词(没有任何空格),则需要在其中搜索任意子字符串。在这种情况下,您需要解析每个文件,以找到匹配的最佳文件。一种使用像博耶摩尔算法那样的算法。见这个这个有关详细信息。这也等同于grep-因为grep在内部使用了类似的东西。但是在返回之前,您仍然可能至少获得了100+ grep(最坏的情况是200万)。

  2. 索引搜索。在这里,您假设文本包含一组单词,并且搜索被限制为固定的单词长度。在这种情况下,将对所有可能出现的单词建立索引。这通常称为“全文搜索”。有许多算法可以做到这一点,并且有许多可以直接使用的开源项目。他们中许多人,还支持通配符搜索,大致搜索等如下:
    一。Apache Lucene:http//lucene.apache.org/java/docs/index.html
    b。OpenFTS:http : //openfts.sourceforge.net/
    c。狮身人面像http://sphinxsearch.com/

如果您需要“固定单词”作为查询,则最有可能的是,方法二将非常快速且有效。


2
这是一个有趣的概念,但是与数据库引擎相比,开发人员似乎不可能轻松快捷地搜索1GB文本数据。比您和我聪明得多的人都在查询优化器上努力做到这一点,并且认为您可以以某种方式更有效地做到这一点有点天真。
maple_shaft

4
@maple_shaft我给出的示例不是RDBMS数据库引擎。如果您想称呼它们,它们更像是“搜索引擎”。从索引(或哈希表)中提取列表与每次查询触发时再次遍历1GB数据之间存在巨大的概念差异。因此,我的建议并非微不足道。
Dipan Mehta

这似乎是一个有趣的想法,但我不知道它如何工作。我将拥有超过2000000个文件,每个文件的大小约为半千字节。还是建议每个文件有多个记录?与数据库有什么区别?
乔治

我不认为这样做一定会比SQL全文索引更好。
柯克·布罗德赫斯特

@Giorgio-是的,这就是全文搜索引擎的工作方式。此处的主要区别是预索引页面与内存中搜索(再次针对每次查询)。
Dipan Mehta

21

您正在寻找的技术是全文索引。大多数RDBMS都具有某种内置功能,可以在这里使用,或者,如果您想获得更高级的功能和/或只在内存中运行它,可以使用Lucene之类的功能。


1
在我看来,任何RDBMS中的全文选项都是一种变通方法,可以使它执行它不适合做的事情:“搜索一些非结构化无关数据”。如果您要构建搜索引擎,则只需不使用RDBMS。它可能适用于小型数据集,但无法进行任何缩放。搜索成堆的非结构化数据不是钉子,因此请勿使用锤子。使用正确的工具完成工作。
Pieter B

8

你考虑过特里吗?基本上,您使用公共前缀来构建树,因此所有以相同字母开头的单词都是相同节点的子代。如果要支持对任何子字符串的匹配,则必须生成某种置换索引并从中构建特里。但是,这可能最终使您的存储需求枯竭。


1
是!我当时在考虑一个树形结构,并且想起有些类似的东西可能适合我,但是我不记得特里的,因为我从未使用过它们。关于存储需求:请记住,我只需要检索前N个条目(例如N = 100),因为用20000个匹配数填充表是没有意义的。因此,特里树的每个节点最多指向N个条目。另外,我忘了提到我需要快速访问,但不需要快速更新,因为数据仅加载一次。排列索引上的trie想法确实可行!
乔治

1
好的答案,但是您要注意,特里树很适合匹配单词的开头,但是如果匹配任何子字符串,它会很快变得复杂且非常大……
Kirk Broadhurst

作为第一个实验,我尝试构建出现在我必须搜索的字符串中的所有子字符串的集合,如果我正确理解的话,这些子字符串对应于trie的路径。我在长度为6的子字符串上遇到了内存不足的异常(JVM的堆为256M)。因此,除非我做错了事,否则我担心此解决方案不可行。
乔治

5

我想在Wyatt Barnett的回答的基础上补充一点,即在适当的列上具有全文索引的RDBMS解决方案将起作用,但是,如果您想利用以前提取的记录的本地缓存,则需要制定计划以利用这些缓存的记录对你有利。

一种选择是收集您不希望从查询中检索的这些记录的唯一标识符,并将它们包括在内(可能在a NOT IN或a中)NOT EXISTS

提醒您,使用NOT INNOT EXISTS往往不便宜,并且可能会对查询性能或查询计划产生负面影响,具体取决于所使用的数据库引擎。在最终查询中运行一个解释计划,以确保所有受影响列上的索引都得到了利用。

在两种方法之间进行性能比较以查看哪种方法更快也没有什么害处。您可能会惊讶地发现,维护本地缓存并显式过滤查询中的缓存可能比微调查询(获取所有记录)的性能差。


maple_shaft和@Wyatt Barnett:非常感谢您的建议。我将不得不做一些阅读并尝试不同的解决方案。并非所有数据库都支持完全索引编制,MySQL(我当前正在使用)支持完全索引编制dev.mysql.com/doc/refman/5.5/en/fulltext-search.html)。我将尝试进行一些测试,然后在此处报告。
乔治

2

以防万一您错过了它。如果将Lucene用于数据库而不是数据库中支持的文本搜索,则在修改数据库时必须格外小心。当必须在数据库和外部资源(Lucene)中进行更改时,如何确保自己具有原子性?是的,可以完成,但是会有很多工作要做。

简而言之,如果将Lucene放在数据模式中,则会失去对DB事务的支持。


1
无论如何,上述问题似乎都不适合RDMS。
Pieter B

1

您考虑过狮身人面像吗?http://sphinxsearch.com(如果您可以使用第三者工具),这将是您要实现的目标的理想选择,它在全文搜索中的效率要比我亲自使用的任何RDBMS高得多。


3
否决票是为了?
twigg '16

1

奇怪的是,没有一个答案使用术语“倒排索引”,即与Apache Lucene和其他解决方案相似的所有解决方案的基础技术。

倒排索引是从单词到文档的映射(“记录级倒排索引”),甚至是文档内精确的单词位置(“单词级倒排索引”)。

AND和OR逻辑运算很容易实现。如果您有精确的单词位置,则可以查找相邻的单词,从而使短语搜索成为可能。

因此,请考虑一个包含(单词,文件,位置)元组的索引。当您有例如(“ inverted”,“ foo.txt”,123)时,只需检查(“ index”,“ foo.txt”,124)是否是索引的一部分以搜索完整的短语“ inverted index” 。

虽然我不建议您从头开始重新实现全文搜索引擎,但是了解诸如Apache Lucene之类的技术的工作方式很有用。

因此,我的建议是学习反向索引的工作方式,并选择使用反向索引的技术,例如Apache Lucene。然后,您至少对可以做什么和不能做什么有深刻的了解。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.