Indexing 使用Redis排序集进行索引

Indexing 使用Redis排序集进行索引,indexing,nosql,redis,set,sorted,Indexing,Nosql,Redis,Set,Sorted,我想就我正在考虑的两种使用Redis排序集实现可搜索索引的方法获得一些反馈和建议 形势和目标 目前,我们在Cassandra中存储了一些键值表,我们希望为它们建立索引。例如,一个表将包含人的记录,Cassandra表将id作为主键,序列化对象作为值。对象将具有诸如first_name、last_name、last_updated等字段 我们想要的是能够进行诸如“姓氏='Smith'和名字>Joel'”、“姓氏

我想就我正在考虑的两种使用Redis排序集实现可搜索索引的方法获得一些反馈和建议

形势和目标

目前,我们在Cassandra中存储了一些键值表,我们希望为它们建立索引。例如,一个表将包含人的记录,Cassandra表将id作为主键,序列化对象作为值。对象将具有诸如first_name、last_name、last_updated等字段

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

我们正在考虑使用Redis实现这一点,因为我们需要能够每分钟处理大量写操作。我阅读了一些常用的Redis排序集的使用方法,并提出了两种可能的实现:

选项1:每个索引的单个排序集

对于按姓氏、姓氏、姓氏更新的索引,我们在Redis中的关键索引下有一个排序集:people:last\u name:first\u name:last\u updated,其中包含格式为last\u name:first\u name:last\u updated:id的字符串。例如:

class Person(odm.StdModel):
    first_name = odm.SymbolField()
    last_name = odm.SymbolField()
    last_update = odm.DateTimeField()
史密斯:乔尔:1372761839.444:0azbjZRHTQ6U8enBw6BJBw

(对于分隔符,我可能会使用“::”而不是“:”或其他更好地处理词典排序的东西,但现在让我们忽略它)

所有项目都将被给予0分,这样排序的集合将只按照字符串本身的字典顺序进行排序。如果我想执行类似“last_name='smith'和first_name<'bob'”的查询,我需要获取列表中位于'smith:bob'之前的所有项目

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

  • 没有基于字符串值选择范围的Redis函数。这个名为ZRANGEBYLEX的特性是由Salvatore Sanfilippo at提出的,但尚未实现,因此我必须使用二进制搜索找到端点,然后自己获取范围(可能使用Lua,或者在应用程序级别使用Python,这是我们用来访问Redis的语言)
  • 如果我们想为索引项添加一个生存时间,那么最简单的方法似乎是有一个定期安排的任务,它遍历整个索引并删除过期的项
  • 选项2:按上次更新的\u排序的小排序集

    这种方法类似,只是我们会有许多更小的排序集,每个都有一个类似时间的值,比如最后更新的分数。例如,对于相同的姓氏、姓氏、姓氏更新索引,每个姓氏、姓氏组合都有一个排序集。例如,键可能是index:people:last_name=smith:first_name=joel,它将为我们称为joel-smith的每个人提供一个条目。每个条目的名称为id,分数为最近更新的值。例如:

    值:0AZBJZRhTq6u8enBw6bBw;分数:1372761839.444

    这样做的主要优点是:(a)搜索除上次更新外的所有字段非常容易,(b)使用ZREMRANGEBYSCORE实现生存时间非常容易

    对我来说,缺点很大,就是:

  • 以这种方式进行管理和搜索似乎要复杂得多。例如,我们需要索引来跟踪它的所有键(例如,如果我们想在某个点进行清理),并以分层的方式进行。像“姓氏<'smith'”这样的搜索需要首先查看所有姓氏的列表以查找在smith之前的姓氏,然后查看其中包含的所有姓氏,然后从其排序集中获取所有项目。换句话说,有很多组件需要构建和担心
  • 收尾

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

  • 我强烈反对为此使用Redis。您将存储大量额外的指针数据,如果您决定执行更复杂的查询,例如,
    SELECT WHERE first_name,例如'jon%'
    ,您将遇到麻烦。如果您想同时搜索两个字段,您还需要设计跨多个列的额外、非常大的索引。基本上,您需要不断地进行黑客攻击并重新设计搜索框架。您最好使用or或其他任何已经构建的框架来完成您正在尝试的工作。Redis非常棒,有很多很好的用途。这不是其中之一

  • 除此之外,请回答您的实际问题:我认为您最好使用第一种解决方案的变体。每个索引使用单个排序集,但只需将字母转换为数字即可。将您的字母转换为一些十进制值。您可以使用ASCII值,或者假设您使用的是英语,则只需按字典顺序将每个字母指定为1-26的值。标准化,使每个字母占用相同的数字长度(因此,如果26是最大的数字,则1将写为“01”)。然后在前面加上一个小数点,并将其作为每个索引的分数(即“hat”
    qs = models.person.filter(first_name='john', last_name='smith')
    
    qs = models.person.filter(first_name=('john','carl'), last_name=('smith','wood'))
    
    //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])
    })