针对字符串匹配优化的数据库/数据源?

时间:2013-02-22 12:55:35

标签: php database datasource string-matching database-performance

我想存储大量(〜数千)字符串,并且能够使用通配符执行匹配。

例如,以下是示例内容:

  • Folder1
  • Folder1/Folder2
  • Folder1/*
  • Folder1/Folder2/Folder3
  • Folder2/Folder*
  • */Folder4
  • */Fo*4

(每行都有附加数据,如标签,但匹配仅针对该密钥)

以下是我希望与数据匹配的示例:

  • Folder1
  • Folder1/Folder2/Folder3
  • Folder3

*在这里是一个通配符,它​​可以是一个不同的字符)

我天真地考虑将它存储在MySQL表中并使用%通配符和LIKE运算符,但MySQL索引仅适用于通配符左侧的字符,在我的情况下它可以在任何地方(即%/Folder3)。

所以我正在寻找一种快速解决方案,可以从 PHP 中使用。我是开放的:它可以是一个单独的服务器,一个使用带正则表达式的文件的PHP库,......

9 个答案:

答案 0 :(得分:1)

您是否考虑过使用MySQL的正则表达式引擎?尝试这样的事情:

SELECT * FROM your_table WHERE your_query_string REGEXP pattern_column

这将返回具有查询字符串匹配的正则表达式键的行。我希望它比运行查询以提取所有数据并在PHP中进行匹配更好。

此处有更多信息:http://dev.mysql.com/doc/refman/5.1/en/regexp.html

答案 1 :(得分:0)

您可能希望使用多核方法在很短的时间内解决搜索问题,我建议使用FPGA进行搜索和匹配,但这可能是最难的方法,请考虑使用CUDA THIS ARTICLE ,您可以在16倍的平常时间内进行搜索,在多核CPU系统中,您可以使用posix或一组计算机来完成工作(例如MPI),您可以调用Gearman服务来运行搜索先进的算法。

答案 2 :(得分:0)

如果是我,我会将关键字段存储两次......一次向前和一次反转(参见mysql的反向功能)。然后,您可以使用left(main_field)和left(reversed_field)搜索索引。如果你在字符串和开头的中间有一个通配符(例如“* Folder1 * Folder2”),它将无法帮助你,但是当你在开头或结尾有一个通配符时它就会出现。

e.g。如果你想搜索* / Folder1然后搜索左边的地方(reverse_field,8)='1redloF /'; for Folder1 / * / FolderX搜索左边(reverse_field,8)='XredloF /'和左边(main_field,8)='Folder1 /'

答案 3 :(得分:0)

如果你的字符串代表某种层次结构(就像在你的示例内容中看起来那样),实际上不是“真正的”文件,但你说你对替代解决方案持开放态度 - 为什么不考虑类似基于文件的索引?

  • 选择新目录,例如myindex
  • 使用字符串键作为location&amp ;;为每个条目创建一个空文件。 myindex
  • 中的文件名

现在您可以使用glob找到匹配项 - 由于分层文件结构,全局搜索应该比搜索所有数据库条目快得多。 如果需要,您可以将结果与MySQL数据相匹配 - 由于您的密钥上的MySQL索引,此操作将非常快。

但请不要忘记更新MySQL数据库中myindexINSERTUPDATE的{​​{1}}结构。

这个解决方案只会在一个庞大的数据集上进行竞争(但不像@Kyle所提到的那么大),而不是宽泛的层次结构。

修改 对不起,这只有在通配符在您的搜索条件中而不在存储的字符串本身中时才有效。

答案 4 :(得分:0)

由于通配符(*)在您的数据中,而不在您的查询中,我认为您应该首先将数据分解成碎片。您应该创建一个索引表,其中包含以下列:

dataGroup INT(11),
exactString varchar(100),
wildcardEnd varchar(100),
wildcardStart varchar(100),

如果您有类似“Folder1 / Folder2”的值,请将其存储在“exactString”中,并将主数据表中的值ID分配给上述索引表中的“dataGroup”。

如果你有一个像“Folder1 / *”这样的值,将“Folder1 /”的值存储到“wildcardEnd”,并再次将主表中的值的id分配给上表中的“dataGroup”字段。

然后,您可以使用以下命令在查询中进行匹配:

indexTable.wildcardEnd = LEFT('Folder1/WhatAmILookingFor/Data', LENGTH(indexTable.wildcardEnd))

这会将搜索字符串('Folder1 / WhatAmILookingFor / Data')截断为“Folder1 /”,然后将其与wildcardEnd字段进行匹配。我假设mysql足够聪明,不会为每一行执行截断,而是从第一个字符开始,并将其与每一行匹配(使用B-Tree索引)。

像“* / Folder4”这样的值会进入“wildcardStart”字段,但会反转。引用Missy Elliot说:“值得吗,让我来做吧 我把我的东西放下来,翻转并反转它“(http://www.youtube.com/watch?v=Ke1MoSkanS4)。所以在”wildcardStart“中存储一个”4redloF /“的值。然后像下面这样的WHERE将匹配行:

indexTable.wildcardStart = LEFT(REVERSE('Folder1/WhatAmILookingFor/Folder4'), LENGTH(indexTable.wildcardStart))

当然,您可以在应用程序逻辑中执行“REVERSE”。

现在关于棘手的部分。像“* / Fo * 4”这样的东西应该分成两个记录:

# Record 1
dataGroup ==> id of "*/Fo*4" in data table
wildcardStart ==> oF/
wildcardEnd ==> /Fo

# Record 2
dataGroup ==> id of "*/Fo*4" in data table
wildcardStart ==> 4

现在,如果匹配某些内容,则必须注意返回dataGroup的每个索引记录以进行完全匹配,并且不会发生重叠。这也可以在SQL中解决,但超出了这个问题。

答案 5 :(得分:0)

数据库不是进行这类搜索的正确工具。您仍然可以使用数据库(任何数据库和任何结构)来存储字符串,但您必须编写代码以在内存中执行所有搜索。从数据库加载所有字符串(几千个字符串实际上并不重要),缓存它们并在它们上运行search \ match算法。

您可能必须自己编写算法代码,因为标准工具对于您要实现的目标来说是一种过度杀伤力,并且无法保证他们能够完全满足您的需求。

我将构建基于通配符的字符串的正则表达式表示,并在输入上运行这些正则表达式。在正确使用正则表达式之前,你的概率必须做一些工作,但这将是最快的方法。

答案 6 :(得分:0)

我建议将密钥及其相关的有效负载读入按字母顺序按字母顺序排序的二叉树表示。如果您的钥匙不是非常“聚集”,那么您可以避免平衡树的(略微额外的)架空建筑。您还可以避免任何树维护代码,因为如果我正确理解您的问题,数据将经常更改,并且最简单的方法是重建树而不是添加/删除/更新节点。读入树的开销类似于执行初始排序,并且搜索您的值的树遍历是直接的,并且比仅针对一堆字符串运行正则表达式更有效。你甚至可以在找到它时发现树中的通配符会导致一些快捷方式来修剪搜索空间。快速搜索显示了许多资源和PHP片段,可以帮助您入门。

答案 7 :(得分:-1)

如果你运行SELECT folder_col, count(*) FROM your_sample_table group by folder_col,你会得到重复的folder_col值(即count(*)是否大于1)?

如果没有,这意味着您可以生成一个可以生成有效sphinx索引的SQL(请参阅http://sphinxsearch.com/)。

答案 8 :(得分:-1)

我不建议在MySQL中对大量数据进行文本搜索。您需要一个数据库来存储数据,但就是这样。搜索使用搜索引擎,如:

这些服务将允许您在眨眼之间进行各种时髦的文本搜索(包括通配符); - )