%title%
Stosowanie tzw. cache’a jest jednym z najprostszych sposobów do osiągnięcia lepszej wydajności programu komputerowego. Redis jest jedną z wielu technologii tego typu. W poniższej notce pokażę Ci małą zajawkę jego możliwości.
- Czym jest cache?
- Cache w języku Java
- Instalacja serwera
- Redis Cli
- Komendy SET i GET
- TYPE
- EXISTS
- KEYS
- DEL
- RENAME
- EXPIRE i TTL
- Praktyczne zastosowanie
- Alternatywy dla Redis
Czym jest cache?
Częstym problemem, występującym w programowaniu jest wydajność Twojego produktu. To dość powszechne, iż wszyscy chcemy korzystać z usług IT w coraz to szybszy i przyjemniejszy sposób. Nikt nie chce czekać na ładowanie się danej aplikacji kilka minut, tak jak w przypadku starych gier komputerowych w latach 90. Dziś choćby kilka sekund przestoju przy korzystaniu z software’u to już za długo. W jaki sposób więc, przyśpieszyć znacznie pracę na Twojej aplikacji?
Jednym z najprostszych sposobów do osiągnięcia lepszej wydajności programu komputerowego jest cache. Czym jest on w takim razie jest? Książkowo to po prostu mechanizm, który zajmuje się lepszym udostępnianiem danych. Specjalnie napisałem 'mechanizm’, ponieważ klasyczny cache jest jednym z komponentów komputera, takich jak karta graficzna czy dysk twardy. Najczęściej spotykanym cachem, jest cache przy procesorze. Zasada jest tu prosta. Pamięć RAM jest pamięcią o sporych rozmiarów ale nie za szybką*. Ponieważ procesor pracuje na małym wycinku danych, inżynierowie pierwszych komputerów wpadli na pomysł, iż można przecież zainstalować małą ale bardzo szybką pamięć pomiędzy pamięcią RAM a procesorem.
Najtrudniejszym zadaniem komputera pozostaje wyselekcjonowanie, dzięki specjalnych algorytmów (np. Last Recent Used***), odpowiednich danych, które prawdopodobnie będą wykorzystywane w obecnej chwili przez procesor. Tego typu zadania nie są trywialne, dlatego też cache procesora jest jednym z najważniejszych parametrów przy zakupie komputera.
Cache w języku Java
Cache’owanie to praktyka również stosowana w przypadku software’u. Idea jest tu podobna jak w przypadku „sprzętowego” cache’a. Programiści gwałtownie odkryli, iż podobną strategię, jak w przypadku procesorów, można stosować także na poziomie samych aplikacji. Jednym ze sposobów przyśpieszania działania określonych operacji, jest trzymanie danych w pamięci. W ten sposób Twój program, nie musi „sięgać” do nich, gdy są dostępne np. poprzez bazę danych, pliki, serwisy, itp. Dzięki temu rozwiązaniu unikasz kosztownego połączenia z zewnętrznymi zasobami.
W przypadku programów Java, najprostszym mechanizmem cache’owania jest po prostu trzymanie danych w formie zmiennych statycznych (tzw. local cache). Banalnie proste prawda? Ale za to niesamowicie szybkie. Niestety minus tego rozwiązania jest konieczność przeładowywania aplikacji przy każdej aktualizacji zmiennych. Takie podejście praktycznie skreśla tą opcję w momencie komercyjnego dewelopmentu.
Należy w takim razie poszukać jakiegoś sprytniejszego sposobu. Jednym z nich jest tzw. distributed cache, czyli zewnętrzna aplikacja, która opiera się o jakiś algorytm cache’owania. Takim przypadkiem może być właśnie framework Redis. Chodź wymaga on komunikacji między Twoją aplikacją a samym cache’m (czyli z tego tytułu jest wolniejszy), to jednak wykorzystywane optymalizacje starają się to zrekompensować (głównie poprzez trzymanie danych w pamięci). Plusem tego rozwiązania jest to, iż może być użyty przez wiele klastrów(instancji) aplikacji i jest zarządzany niezależnie.
Instalacja serwera
Aby skorzystać z serwera Redis wpierw musisz go zainstalować na swoim systemie. Choć on sam jest wspierany zarówno na Windows jak i MacOs, to w tej notce skupię się na obsługiwaniu go poprzez Linux. Nie jest on standardowo zawarty w Linuxowym repozytorium APT, więc wpierw dodaj go wywołując komendy:
curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/redis.listTeraz zaktualizuj APT i zainstaluj program
sudo apt-get update sudo apt-get install redisOdpal teraz serwer wywołując:
redis-serverJeśli cała procedura wykonała się poprawnie, to Cli powinno być dostępne poprzez komendę redis-cli. W razie wątpliwości, czy serwer wystartował możesz go odpytać, używając:
redis-cli pingRedis Cli
Narzędzie to umożliwia też komunikację z serwerem poprzez linię komend. Podaję poniżej kilka z nich:
Biblioteka też posiada całkiem rozbudowany struktur danych. Oczywiście sam cache wciąż pozostanie tylko i wyłącznie hashmapą, jednak wewnątrz niej może zawierać kolejne struktury.
Do wyboru są tu następujące kolekcje m. in.:
- HashMap
- List
- Set
- SortedSet
- BitMap
Podstawowe użycie cache jednak będzie się ograniczało jedynie poprzez przetrzymywanie odpowiednich wartości pod konkretnym kluczem. Pokażę Ci teraz jak korzysta się z linii komend Redis-Cli. W praktyce te wszystkie komendy będą obsługiwane już automatycznie poprzez różnego biblioteki w Twojej aplikacji, jednak uważam, iż warto poznać samo działanie cache’a, tak aby rozumieć, co się dzieje „podspodem”.
Komendy SET i GET
Pierwsze polecenie, o którym musisz wiedzieć jest SET. Przypisuje ono konkretnemu kluczowi wartość.
SET car ToyotaPowyższa komenda zapisała w pamięci wartość 'Toyota’ (typu String) do klucza o nazwie 'car’. jeżeli chcę pobrać co przechowuje zmienna 'car’, wystarczy skorzystać z komendy GET. Każdy klucz jest unikalny, więc jeżeli przypiszesz do 'car’ nową wartość to poprzednia zostanie zastąpiona.
Czasami dane przechowywane w cache’u powinny być ważne przez określony czas, np. przez kilka sekund. Można to osiągnąć poprzez flagi EX (czas ważności będzie tu ustawiony w sekundach) lub PX (w milisekundach).
SET sport swimming EX 10Po wygaśnięciu ważności klucz będzie wyświetlał (nil), czyli brak wartości (lub inaczej pusty set).
TYPE
Jeśli chcesz upewnić się jakiego typu jest konkretna wartość klucza, możesz skorzystać z TYPE. jeżeli klucz nie posiada żadnych danych, to zwróci none.
TYPE carEXISTS
Czasami zamiast odczytywać wartość w mapie, chcesz po prostu sprawdzić czy ona istnieje. Komenda ta zwraca 1 gdy wartość istnieje, albo 1 jeżeli nie.
EXISTS carW moim przypadku rezultat na konsoli będzie:
(integer) 1W ten sam sposób można sprawdzić więcej niż jeden klucz. Niestety rezultatem w tym przypadku będzie liczba całkowita, oznaczająca ile z wypisanych przez Ciebie kluczy posiada wartości. Jednak nie pokaże, które z nich są puste a które nie.
EXISTS sport carW tym ćwiczeniu 'car’ ma przypisaną informację, w przeciwieństwie do 'sport’, którego wartość wygasła po 10 sekundach. Dlatego też komenda EXIST wskaże rezultat 1.
KEYS
Czasami nie wiesz, jakie klucz są przechowywane w cache. Wyświetlanie ich można uzyskać wpisując KEYS oraz odpowiedni regex. Najprostszym przykładem jest wywołanie:
KEYS *Tutaj pokazane zostaną wszystkie klucze w pamięci.
DEL
Oprócz dodawania wartości do mapy, możesz też je usuwać. Służy do tego komenda DEL.
DEL carWywołanie tej akcji zwróci 1, co oznacza prawidłowe usunięcie. jeżeli pomylisz się i spróbujesz usunąć nieistniejący klucz, to zwrócone zostanie 0.
RENAME
Możesz też podmienić nazwę klucza:
RENAME car brandEXPIRE i TTL
Jeśli potrzebujesz dodać czas istnienia przechowywanych danych, możesz dodać też taką informację do istniejących kluczy.
EXPIRE brand 100Po upływie 100 sekund wartość klucza zniknie z cache’a. Gdy potrzebujesz sprawdzić ile jeszcze czasu zostało do jej usunięcia, wywołaj TTL.
TTL brandGdy sprawdzany będzie klucz, nie będzie miał wartości, to TTL zwróci -1. Natomiast dla nieistniejącego klucza pokazany zostanie -2.
To tylko podstawowe operacje, które możesz przeprowadzać na cache’u. jeżeli interesuje Cię pełna lista komend dostępnych przez narzędzie, to zachęcam zapoznać się z oficjalną dokumentacją*****.
Praktyczne zastosowanie
Jednym z częstych zastosowań Redis dla programistów Java jest używanie go jako pamięci podręcznej dla zapytań SQL. Jeden z najpopularniejszych frameworków opierających się na JPA. Hibernate, korzysta z dwóch mechanizmów cechowania. Pierwszy, zwany first-level cache, służy do ograniczenia nadmiernego pytania bazę danych o te same informacje. Idea jest tu bardzo prosta, jeżeli wszystkie zapytania są realizowane dzięki Hibernate, a dana wartość została już raz pobrana, to zostanie ona zapamiętana. Natomiat jeżeli bezpośrednio zmienisz ją w bazie danych (a nie poprzez framework), to Hibernate nie zauważy tej zmiany (wtedy należy wymusić pobranie danych jeszcze raz).
Powyższa optymalizacja pozwala zaoszczędzić wiele czasu, ale nie zawsze jest wystarczająca. Second-level cache to pamięć podręczna, która nie działa automatycznie. Wymaga już zaprogramowania, które dane powinny być zapamiętane, kiedy należy je uaktualnić, itp. Domyślnie second-level cache w Hibernate korzysta z rozwiązania o nazwie Ehcache. W większości przypadku jest on wystarczający do optymalizacji zapytań sql, jednak jeżeli szukać naprawdę wysokiej wydajności rozwiązania, to Redis na pewno jest warty rozważenia.
Alternatywy dla Redis
Redis i Ehcache to niejedyne opcje, jeżeli chodzi o cache’owanie. Istnieją także np. Hazelcast, Reddison, czyli klient Redis napisany w Javie, Memcache, Couchbase, Dynamodb, etc. Warto przeanalizować swoje potrzeby w projekcie, aby dobrać odpowiednią bibliotekę, która spełni wszystkie potrzeby w Twojej aplikacji. Mimo wszystko Redis jest w tej chwili jednym z najlepszych i najpopularniejszych rozwiązań tego typu, dlatego warto się z nim zapoznać i rozważyć do wykorzystania w swojej pracy.
*To oczywiście kwestia względna, bo to co było „szybkie” 10 lat temu, jest ekstremalnie wolne obecnie.
**Twórcą idei szybkiej pamięci jest Maurice Wilkes
***Wszystkie rodzaje cache’a opisane na stronie Oracle.
****JPA (Java Persistence API) to standard mapowania obiektowo-relacyjnego
*****Dokumentacja projektu Redis: https://redis.io/commands/
Jeśli chcesz dowiedzieć się więcej o Linuksie, zajrzyj tutaj: Narzędzia programisty #2: Linux