2011-04-17 48 views
4

为了学习目的,我试图在Redis中编写一个简单的结构化文档存储库。在我的示例应用程序中,我将数百万个文档编入索引,这些文档看上去有点像以下内容使用Redis进行复合查询

<book id="1234"> 
    <title>Quick Brown Fox</title> 
    <year>1999</year> 
    <isbn>309815</isbn> 
    <author>Fred</author> 
</book> 

我正在写一个小的查询语言,让我说YEAR = 1999 AND TITLE="Quick Brown Fox"(再次,只是为了我的学习,我不在乎,我重新发明轮子!),应返回的ID的匹配文件(在这种情况下为1234)。表达式的ANDOR可以任意嵌套。

对于我生成密钥如下

BOOK_TITLE.QUICK_BROWN_FOX = 1234 
BOOK_YEAR.1999 = 1234 
我使用 SADD形式 KEYNAME.VALUE = { REFS }到扑通在一系列套这些文件

每个文档。

当我进行查询时,我将表达式解析为AST。一个简单的表达式,例如YEAR=1999直接映射到一个SMEMBERS命令,该命令让我返回一组匹配文档。但是,我不确定如何最有效地执行AND和OR部分。

给定的查询,如:

(TITLE=Dental Surgery OR TITLE=DIY Appendectomy) 
    AND 
(YEAR = 1999 AND AUTHOR = FOO) 

我目前做如下要求的Redis来回答这些疑问。

-- Stage one generates the intermediate results and returns RANDOM_GENERATED_KEY3 
SUNIONSTORE RANDOMLY_GENERATED_KEY1 BOOK_TITLE.DENTAL_SURGERY BOOK_TITLE.DIY_APPENDECTOMY 
SINTERSTORE RANDOMLY_GENERATED_KEY2 BOOK_YEAR.1999 BOOK_YEAR.1998 
SINTERSTORE RANDOMLY_GENERATED_KEY3 RANDOMLY_GENERATED_KEY1 RANDOMLY_GENERATED_KEY2 

-- Retrieving the top level results just requires the last key generated 
SMEMBERS RANDOMLY_GENERATED_KEY3 

当我遇到一个AND我用SINTERSTORE基于两个子键(同样地,对于OR我用SUNIONSTORE)。我随机生成一个密钥来存储结果(并设置一个短的TTL,所以我不会用Cruft填充Redis)。在这一系列命令结束时,返回值是我可以用来检索SMEMBERS的结果的一个键。我使用商店功能的原因是我不想将所有匹配的文档引用传输回服务器,因此我使用临时密钥将结果存储在Redis实例上,然后仅将匹配结果返回到结束。

我的问题很简单,这是使用Redis作为文档存储的最佳方式吗?

+0

这种方法有多快? – mixdev 2011-05-19 12:34:55

+0

我遵循这种方法,并且发现它非常快(与Redis相关的大多数事情一样)。你可以做的很酷的事情之一是缓存中间键以获得缓存的查询片段(如下面的Tom所建议的)。为了避免在病态情况下存储中间密钥(例如,在整个文档集中带有AND和OR的空集),我还对和和进行了一些优化。 – 2011-05-19 18:20:54

回答

2

我对排序集使用了类似的方法来实现全文索引。总体方法很好,尽管你可以做一些相当简单的改进。

  • 而不是使用随机生成的密钥,您可以使用查询(或其简短形式)作为关键。这可以让您重用已经计算好的集合,如果跨两个大集合进行查询(通常以类似的方式进行组合)查询,这可以显着提高性能。
  • 将标题处理为完整的字符串将导致大量的单一成员集。如果你真的需要它,在标题中索引单个单词并过滤最终结果以进行精确匹配可能会更好。
+0

谢谢!我曾想过重新使用这些键,希望这可以帮助查询片段缓存。作为一个完整字符串的标题好点,我将不得不考虑我想如何匹配这些。 – 2011-04-18 20:34:05