2013-07-02 44 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]) 
})