Skróty Redis są (co jest dość intuicyjne!) skrótami, które mapują nazwy ciągów na wartości ciągów. Są to zasadniczo nazwane kontenery unikalnych pól i ich wartości. Są idealnym sposobem na przedstawienie obiektu jako struktury danych Redis. Zgodnie z oczekiwaniami zapewniają one podstawowe operacje w stałym czasie, takie jak pobieranie, ustawianie, istnienie itp. Dostępnych jest również kilka zaawansowanych operacji. Pełna lista poleceń haszujących znajduje się tutaj.
Weźmy to na przejażdżkę z redis-cli .
# hmset key field value [field value ...] : Insert elements in a hash. O(N), N is # of field being set 127.0.0.1:6379> hmset std:101 name "John Smith" dob "01-01-2000" gender M active 0 cgpa 2.9 OK # hgetall key : key all keys and values in the hash. O(N), N is size of hash 127.0.0.1:6379> hgetall std:101 1) "name" 2) "John Smith" 3) "dob" 4) "01-01-2000" 5) "gender" 6) "M" 7) "active" 8) "0" 9) "cgpa" 10) "2.9" 127.0.0.1:6379> hmset std:102 name "Jane" name "Ann" OK # If duplicates are found, only the last set is valid 127.0.0.1:6379> hgetall std:102 1) "name" 2) "Ann" # hexists key field: does field exist in the hash with key. O(1) 127.0.0.1:6379> hexists std:102 cgpa (integer) 0 # hincrby key field increment: Increment the integer field by increment. O(1) 127.0.0.1:6379> hincrby std:101 active 1 (integer) 1 # hget key field : the value for field in the hash stored at key. O(1) 127.0.0.1:6379> hget std:101 active 1) "1" # If field doesn't exist, hincrby sets it to 0 and then applies increment 127.0.0.1:6379> hincrby std:102 active 2 (integer) 2 # hmget key field [field ...]: the values of the fields requested for the hash with key. O(N), N is # of keys requested 127.0.0.1:6379> hmget std:102 active 1) "2" # hincrbyfloat key field increment: Increment the float value in field by increment. O(1) 127.0.0.1:6379> HINCRBYFLOAT std:101 cgpa 1.0 "3.9" # HSETNX key field value: set field to value if not alread set. O(1) 127.0.0.1:6379> hsetnx std:102 cgpa 4.0 (integer) 1 127.0.0.1:6379> hget std:102 cgpa "4.0" # hlen key: number of fields in the hash. O(1) 127.0.0.1:6379> hlen std:101 (integer) 5 # hkeys key : all fields in the hash. O(N), N is size of hash 127.0.0.1:6379> hkeys std:101 1) "name" 2) "dob" 3) "gender" 4) "active" 5) "cgpa"
Jak przyzwyczailiśmy się do naszego hostingu dla Redis™* jako serwera struktury danych, widzimy, że Redis zapewnia dość przydatne i zaawansowane operacje na hashach.
Wewnętrzne
Podobnie jak zestawy Redis, skróty Redis są również implementowane jako słowniki. Słowniki w Redis są zaimplementowane jako tablice mieszające, które używają funkcji mieszającej MurmurHash2 i powiększają się dzięki stopniowej zmianie rozmiaru. Kolizje skrótów są obsługiwane przez tworzenie łańcuchów. Więcej szczegółów można znaleźć w implementacji słownika Redis pod adresem dict.c.
Podobnie jak w przypadku zestawów, istnieje optymalizacja pamięci dla mniejszych skrótów. Ta struktura danych nosi nazwę ziplist (hasze zostały zoptymalizowane przy użyciu innej struktury danych o nazwie zipmap przed Redis 2.6) w implementacji Redis. Zasadniczo jest to specjalnie zakodowana, podwójnie połączona lista, zoptymalizowana pod kątem oszczędności pamięci. Dane, a także wskaźniki są przechowywane w trybie inline. Ziplist służy również do optymalizacji przechowywania mniejszych posortowanych zestawów i list. Hash po spłaszczeniu do takiej listy wygląda mniej więcej tak:[klucz1, wartość1, klucz2, wartość2, ...]. Jak to jest bardziej wydajne niż zwykłe klucze? Skróty z kilkoma kluczami można sprytnie upakować w tę liniową strukturę podobną do tablicy (tj. Listę zip), jednocześnie gwarantując zamortyzowaną wydajność O(1) dla pobierania i ustawiania. Oczywiście nie nadąża to za wzrostem liczby pól haszujących. Gdy hash rośnie, jest konwertowany do standardowej struktury słownika, aby zachować wydajność O(1), a oszczędność miejsca jest tracona. Parametry konfiguracyjne Redis kontrolujące tę transformację to:
- list-max-ziplist-entries default (512):Zmień na standardową reprezentację, jeśli hash przekroczy ten limit.
- list-max-ziplist-value default (64):Zmień na standardową reprezentację, jeśli największy element w hashu staje się większy niż ten limit.
Więcej szczegółów można znaleźć w kodzie i komentarzach w implementacji, które można znaleźć tutaj. Oszczędności pamięci wynikające z zastosowania tej specjalnej optymalizacji są znaczne. Omówimy to bardziej szczegółowo w następnej sekcji.
Optymalizacja pamięci
Jednym z dobrze znanych zaleceń dotyczących oszczędzania pamięci podczas korzystania z Redis jest używanie skrótów zamiast zwykłych ciągów. Jest to ważny przypadek użycia do wykorzystania mocy skrótów Redis w rzeczywistych aplikacjach. Z oficjalnej dokumentacji Redisa dotyczącej optymalizacji pamięci:
W miarę możliwości używaj skrótów
Małe skróty są zakodowane na bardzo małej przestrzeni, więc powinieneś próbować reprezentować swoje dane za pomocą skrótów za każdym razem, gdy jest to możliwe. Na przykład, jeśli masz obiekty reprezentujące użytkowników w aplikacji internetowej, zamiast używać różnych kluczy dla imienia, nazwiska, adresu e-mail, hasła, użyj jednego skrótu ze wszystkimi wymaganymi polami.
Ten post następnie proponuje jeden sposób mapowania zakresu obiektów na zestaw skrótów, aby wykorzystać oszczędność pamięci. Instagram w bardzo popularnym poście na blogu opisuje zastosowanie podobnej techniki, która pomogła im osiągnąć rzędy wielkości potencjalnych oszczędności. Oto kolejny blog, który próbuje zmierzyć korzyści z optymalizacji.
Aplikacje
- Hasze Redis są naturalnie dostosowane do przechowywania obiektów:sesji, użytkowników, odwiedzających itp. To sprawia, że jest to jedna z kluczowych struktur danych dostarczanych przez Redis.
- W postaci zoptymalizowanej pod kątem pamięci jest to doskonały wybór do buforowania dużych ilości danych.
Spis adresów obiektów
Ponieważ optymalizacja pamięci jest ważnym przypadkiem użycia skrótów, omówmy przykład podobny do wdrożenia na Instagramie, aby pokazać, jak korzystać z funkcji oszczędzania pamięci skrótów Redis. Załóżmy, że mamy ogromne wdrożenie magazynu adresowanego treścią (CAS) z setkami milionów przechowywanych obiektów. Lokalizacja każdego obiektu to ciąg haszujący. Zamierzamy opracować system wyszukiwania, aby znaleźć lokalizację obiektu na podstawie jego identyfikatora. Typowym sposobem na zrobienie tego w Redis będzie użycie ciągu.
set object:14590860 "007f80f0a62408..."
set object:11678 "009f80abcd0a60..."
...
To podejście działa dobrze. Jednak ponieważ liczba obiektów, które posiadamy, jest ogromna, będziemy potrzebować dużo pamięci dla Redis. Chcemy robić lepiej. Przyjrzyjmy się zoptymalizowanemu podejściu haszującemu do tego problemu. Będziemy musieli wybrać odpowiednie wartości dla list-max-ziplist-entries i list-max-ziplist-value . Prawidłowa wartość list-max-ziplist-value jest dowolną maksymalną długością ciągu skrótu adresu pamięci. Wartość list-max-ziplist-entries musi być wystarczająco niski i będzie zależeć od liczby wszystkich wiader mieszających, które chcemy utworzyć. Najlepiej będzie to obliczyć empirycznie. Dla m.in. na 100 milionów obiektów moglibyśmy użyć 100 tys. hashów. Maksymalna liczba wpisów w tym przypadku będzie wynosić 100m / 100k =1000. Logika aplikacji decydująca o tym, do którego skrótu adres pamięci obiektu ma trafić, może być:podziel identyfikator obiektu przez 100k i odrzuć resztę. W ten sposób obiekt ID 14590860 zostanie umieszczony w hashu (14590860/100k) =145 tj.
hset object:145 14590860 "007f80f0a62408..."
hget object:145 14590860
> "007f80f0a62408..."
Ta implementacja będzie nie tylko znacznie lżejsza pod względem pamięci, ale powinna również zapewnić dobrą lokalizację pamięci podręcznej.
Oto nasze inne posty z serii dotyczącej struktury danych Redis.
- Zestawy Redis
- Mapy bitowe Redis
- Posortowane zestawy ponownie