2013-07-02 31 views
6

Chciałbym uzyskać informacje zwrotne i sugestie dotyczące dwóch podejść, które rozważam w celu wdrożenia indeksów z możliwością przeszukiwania za pomocą sortowanych zestawów Redis.Indeksowanie za pomocą sortowanych zestawów Redis

Sytuacja i obiektywne

Obecnie mamy kilka tabel klucz-wartość jesteśmy przechowywanie w Cassandry, a które chcielibyśmy mieć indeksów. Na przykład jedna tabela zawiera wpisy osób, a tabela Cassandra będzie miała identyfikator jako klucz podstawowy, a obiekt serializowany jako wartość. Obiekt miałby pola takie jak first_name, last_name, last_updated i inne.

Chcemy, aby móc zrobić wyszukiwania takich jak "LAST_NAME = 'Smith' I first_name> 'Joel'", "LAST_NAME < 'Aaronson'", „LAST_NAME = 'Smith' I first_name = 'Winston' " i tak dalej. Poszukiwania powinny dawać identyfikatory meczów, abyśmy mogli odzyskać obiekty z Cassandry. Myślę, że powyższe wyszukiwania można wykonać za pomocą pojedynczego indeksu, posortowanego leksykograficznie według nazwy last_name, first_name i last_updated. Jeśli potrzebujemy wyszukiwań przy użyciu innej kolejności (np. "First_name =" Zeus ""), możemy mieć podobny indeks, który pozwoliłby na takie (np. First_name, last_updated).

Szukamy możliwości korzystania z usługi Redis, ponieważ musimy obsługiwać dużą liczbę zapisów na minutę. Czytałem się na pewnych wspólnych sposobów Redis posortowane zestawy są używane i pochodzą z dwóch możliwych wdrożeń:

Wariant 1: pojedynczy zestaw posortowanych na indeksie

Dla naszego indeksu przez LAST_NAME FIRST_NAME, last_updated, mielibyśmy posortowany zestaw w Redis pod kluczowymi indeksami: ludzie: last_name: first_name: last_updated, który zawierałby napisy w formacie last_name: first_name: last_updated: id. Na przykład:

smith: Joel: 1372761839.444: 0azbjZRHTQ6U8enBw6BJBw

(dla separatora mogę wykorzystać „::” zamiast „:”, czy coś innego, aby lepiej współpracować z leksykograficznego zamawiania, ale spójrzmy prawdzie w ignorować faktu, że dla teraz)

Wszystkie pozycje będą miały wynik 0, dzięki czemu posortowany zestaw zostanie posortowany leksykograficznie przez same napisy. Jeśli chciałbym wykonać zapytanie takie jak "last_name =" smith "AND first_name <" bob "", potrzebowałbym uzyskać wszystkie pozycje z listy, które pojawiają się przed "smith: bob".

O ile mogę powiedzieć, istnieją następujące wady tego podejścia:

  1. nie ma funkcji Redis, aby wybrać zakres na podstawie wartości strun. Ta funkcja, zwana ZRANGEBYLEX, została zaproponowana przez Salvatore Sanfilippo pod numerem https://github.com/antirez/redis/issues/324, ale nie jest zaimplementowana, więc musiałbym znaleźć punkty końcowe za pomocą wyszukiwania binarnego i uzyskać zasięg osobiście (być może przy użyciu Lua lub na poziomie aplikacji z Pythonem, który jest językiem, którego używamy, aby uzyskać dostęp do Redis).
  2. Jeśli chcemy uwzględnić czas przeznaczony na wprowadzanie indeksu, wydaje się, że najprostszym sposobem jest wykonanie regularnie zaplanowanego zadania, które przechodzi przez cały indeks i usuwa wygasłe pozycje.

Opcja 2: małe zestawy sortowane, posortowane według LAST_UPDATED

Takie podejście byłoby podobne, z wyjątkiem mielibyśmy wielu mniejszych, sortowane zestawy, z których każdy ma wartość czasu, jak takich jak LAST_UPDATED dla wyników. Na przykład, dla tej samej nazwy last_name, first_name, last_updated, mamy posortowany zestaw dla każdej kombinacji nazwa_ostatniej, pierwsza_nazwa. Na przykład kluczem może być indeks: people: last_name = smith: first_name = joel, i będzie miał wpis dla każdej osoby, którą nazwaliśmy Joel Smith. Każdy wpis będzie miał jako nazwę id, a jego wynik będzie wartością last_updated. Np .:

wartość: 0azbjZRHTQ6U8enBw6BJBw; wynik: 1372761839.444

Głównymi zaletami są: (a) wyszukiwania, w których wiemy, że wszystkie pola z wyjątkiem last_updated byłyby bardzo łatwe, oraz (b) wdrożenie czasu życia byłoby bardzo łatwe, za pomocą ZREMRANGEBYSCORE.

Wadą, która wydaje się bardzo duża dla mnie jest:

  1. Nie wydaje się być dużo bardziej złożoność zarządzania i szukając w ten sposób. Na przykład, potrzebowalibyśmy indeksu, aby śledzić wszystkie jego klucze (na przykład, jeśli chcemy na przykład posprzątać) i zrobić to w sposób hierarchiczny. Wyszukiwanie takie jak "last_name <" smith "" wymagałoby najpierw spojrzenia na listę wszystkich nazwisk, aby znaleźć te, które pojawiają się przed kowalem, a następnie dla każdego z tych, którzy patrzą na wszystkie imiona, które zawiera, a następnie dla każdego z nich pobieranie wszystkich przedmiotów z posortowanego zestawu. Innymi słowy, wiele komponentów do budowania i martwić się.

Zamykając

Więc Wydaje mi się pierwsza opcja byłaby lepsza, mimo jego wad. Byłbym bardzo wdzięczny za wszelkie uwagi dotyczące tych dwóch lub innych możliwych rozwiązań (nawet jeśli są one takie, że powinniśmy używać czegoś innego niż Redis).

Odpowiedz

7
  1. Zdecydowanie odradzam korzystanie z tego programu. Będziesz przechowywać mnóstwo dodatkowych danych wskaźnikowych, a jeśli kiedykolwiek zdecydujesz, że chcesz wykonać bardziej skomplikowane zapytania, takie jak: SELECT WHERE first_name LIKE 'jon%', napotkasz kłopoty. Musisz także opracować dodatkowe, bardzo duże indeksy, które przechodzą przez wiele kolumn, w przypadku, gdy chcesz wyszukać dwa pola w tym samym czasie. Zasadniczo będziesz musiał hakować i przebudowywać strukturę wyszukiwania. Znacznie lepiej byłoby użyć Elastic Search lub Solr lub dowolnej innej struktury, która została już zbudowana, aby zrobić to, co próbujesz zrobić. Redis jest niesamowity i ma wiele dobrych zastosowań. To nie jest jedna z nich.

  2. Ostrzegaj na bok, aby odpowiedzieć na Twoje aktualne pytanie: Myślę, że najlepiej będzie Ci służyć przy użyciu wariantu pierwszego rozwiązania. Użyj jednego posortowanego zestawu na indeks, ale po prostu konwertuj swoje litery na liczby. Konwertuj litery do pewnej wartości dziesiętnej. Możesz użyć wartości ASCII lub po prostu przypisać każdą literę do wartości 1-26 w porządku leksykograficznym, zakładając, że używasz angielskiego. Standaryzuj, aby każda litera zajmowała tę samą długość liczbową (tak, jeśli 26 jest twoją największą liczbą, 1 będzie napisane "01"). Następnie dodaj je razem z kropką dziesiętną z przodu i użyj tego, ponieważ twój wynik na indeks (tj. "Kapelusz" będzie wynosił ".080120"). Umożliwi to uporządkowane mapowanie 1-do-1 pomiędzy słowami i tymi liczbami. Podczas wyszukiwania, konwertuj z liter na liczby, a następnie będziesz mógł korzystać z wszystkich ładnie posortowanych funkcji zestawu Redis, takich jak ZRANGEBYSCORE, bez konieczności przepisywania ich.Funkcje Redisa są napisane bardzo, bardzo optymalnie, więc lepiej jest używać ich, gdy tylko jest to możliwe, zamiast pisać własne.

4

Można użyć do tego mojego projektu python-stdnet, wykonując wszystkie operacje indeksowania. Na przykład:

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

Gdy model jest registered with a redis backend, można to zrobić:

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

jak również

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

i wiele więcej

Filtrowanie jest szybki ponieważ wszystkie identyfikatory są już w zestawach.

+0

Sieć [help o tym, jak nie być spamerem] (http://stackoverflow.com/help/promotion) jest oczywiste, że „trzeba ujawniać przynależność w odpowiedzi.” Odpowiednio zredagowałem twoją odpowiedź. – Louis

0

Możesz sprawdzić redblade, może to być indeks konserwacji automatycznie dla ciebie i jest napisany przez 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]) 
})