2013-07-02 43 views
6

我想获得关于两种方法的反馈和建议,我正在考虑使用Redis排序集来实现可搜索索引。使用Redis排序集索引

形势和客观

目前,我们有我们在卡桑德拉存储一些键值表,我们希望有索引。例如,一个表将包含人员的记录,而Cassandra表将以id作为其主键,并将序列化对象作为值。该对象应具有诸如first_name,last_name,last_updated等字段。

我们想要的是能够进行搜索,如“last_name ='Smith'AND first_name>'Joel'”,“last_name <'Aaronson'”,“last_name ='Smith'AND first_name ='Winston' “ 等等。搜索应该产生匹配的ID,以便我们可以从Cassandra中检索对象。我在想上面的搜索可以用单个索引来完成,按last_name,first_name和last_updated按字典顺序排序。如果我们需要使用不同顺序进行搜索(例如“first_name ='Zeus'”),我们可以使用类似的索引来允许这些索引(例如first_name,last_updated)。

我们正在研究如何使用Redis,因为我们需要能够每分钟处理大量的写入操作。我读了一些常见的方式Redis的排序是用来套,并拿出了两种可能的实现:

选项1:每个指数

对于由姓氏,FIRST_NAME我们的索引单个有序集合, last_updated,我们将在Redis下的关键索引下有一个有序集:people:last_name:first_name:last_updated,它将包含格式为last_name:first_name:last_updated:id的字符串。例如:

史密斯:乔尔:1372761839.444:0azbjZRHTQ6U8enBw6BJBw

(作为隔离我可能会使用“::”,而不是“:”或别的东西与词典顺序更好的工作,但我们忽略了现在)

这些项目都将被赋予0分,以便排序后的集合将按照字符串本身按字典顺序排序。如果我然后想要做一个像“last_name ='smith'AND first_name <'bob'”的查询,我需要获得列表中所有在'smith:bob'之前的项目。

据我所知,有以下缺点这种方法:

  1. 没有Redis的功能选择基于字符串值的范围。此功能称为ZRANGEBYLEX,由Salvatore Sanfilippo在https://github.com/antirez/redis/issues/324提出,但未实现,所以我将不得不使用二进制搜索找到端点并自己获取范围(可能使用Lua,或者在应用程序级别使用Python是我们用来访问Redis的语言)。
  2. 如果我们想要包含索引条目的生存时间,似乎最简单的方法是定期执行一项计划任务,该任务会遍历整个索引并删除过期的项目。

选项2:小的有序集合,由LAST_UPDATED

排序此方法将是类似的,除了我们将有许多,更小的,排序集合,每个具有如LAST_UPDATED一个时间样值为分数。例如,对于相同的last_name,first_name,last_updated索引,我们将为每个last_name,first_name组合有一个有序集。例如,关键字可能是索引:people:last_name = smith:first_name = joel,并且它对每个我们称为Joel Smith的人都有条目。每个条目的名称都是id,其分数是last_updated的值。例如:

value:0azbjZRHTQ6U8enBw6BJBw;得分:1372761839.444

这样做的主要优点是(a)搜索我们知道除last_updated之外的所有字段将非常容易,并且(b)使用ZREMRANGEBYSCORE可以非常容易地实现生存时间。

的缺点,这似乎是非常大的,在我看来:

  1. 似乎那里多了很多管理和搜索这样的复杂性。例如,我们需要索引来跟踪其所有键(例如,我们希望在某个时候清理)​​,并以分层方式执行此操作。搜索“last_name <'smith'”需要首先查看所有姓氏的列表以找到史密斯之前出现的那些姓氏,然后对于每个查看它所包含的所有姓氏的人,然后对于每个人从其排序的集合中获取所有项目。换句话说,需要构建和担心的很多组件。

结束语

所以在我看来,第一种选择会更好,尽管它的缺点。我非常感谢任何有关这两种或其他可能解决方案的反馈(即使他们是我们应该使用Redis以外的其他解决方案)。

回答

7
  1. 我强烈建议不要使用Redis。您将存储大量额外的指针数据,如果您决定要执行更复杂的查询,例如SELECT WHERE first_name LIKE 'jon%',那么您将遇到麻烦。如果您想同时搜索两个字段,您还需要设计跨多列的额外非常大的索引。您基本上需要继续黑客攻击并重新设计搜索框架。使用Elastic SearchSolr或其他任何已经构建好的框架来完成你想要做的事,你会更好。 Redis非常棒,有很多好用处。这不是其中的一个。

  2. 除了警告之外,要回答您的实际问题:我认为您最好使用第一种解决方案的变体。每个索引使用一个单独的排序集,但只是将您的字母转换为数字。将您的字母转换为一些十进制值。您可以使用ASCII值,或者按字典顺序将每个字母指定为1-26值(假设您使用的是英文)。标准化,以便每个字母占用相同的数字长度(所以,如果26是你最大的数字,1将被写为“01”)。然后在前面加上一个小数点,并用它作为每个索引的分数(即“帽子”将是“.080120”)。这可以让你在单词和这些数字之间有一个正确的1对1映射。当您搜索时,将字母转换为数字,然后您就可以使用Redis的所有很好的排序集函数,如ZRANGEBYSCORE,而无需重写它们。Redis的功能非常非常优化,所以你尽可能使用它们而不是自己写。

4

你可以使用我的项目python-stdnet,因为它为你做了所有的索引。例如:

class Person(odm.StdModel): 
    first_name = odm.SymbolField() 
    last_name = odm.SymbolField() 
    last_update = odm.DateTimeField() 

一旦该模型是registered with a redis backend,你可以这样做:

qs = models.person.filter(first_name='john', last_name='smith') 

以及

qs = models.person.filter(first_name=('john','carl'), last_name=('smith','wood')) 

和更

过滤速度快因为所有的ID都已经在集合中。

+0

的[关于如何不被垃圾邮件发送者的帮助(http://stackoverflow.com/help/promotion)显然,“你必须在你的答案透露你联系。”我相应地编辑了你的答案。 – Louis

0

您可以检查redblade,它可以自动为您维护索引,它由Node.JS编写。

//define schema 
redblade.schema('article', { 
    "_id"   : "id" 
    , "poster"  : "index('user_article')" 
    , "keywords" : "keywords('articlekeys', return +new Date()/60000 | 0)" 
    , "title"  : "" 
    , "content"  : "" 
}) 


//insert an article 
redblade.insert('article', { 
    _id  : '1234567890' 
    , poster  : 'airjd' 
    , keywords : '信息技术,JavaScript,NoSQL' 
    , title  : '测试用的SLIDE 标题' 
    , content : '测试用的SLIDE 内容' 
}, function(err) { 

}) 


//select by index field or keywords 
redblade.select('article', { poster:'airjd' }, function(err, articles) { 
    console.log(articles[0]) 
}) 

redblade.select('article', { keywords: 'NoSQL' }, function(err, articles) { 
    console.log(articles[0]) 
})