Narzędzia programisty #4: Redis cache

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?

Częstym problemem, występującym w programowaniu jest wydajność Twojego produktu. To dość powszechne, że 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ś nawet 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ł, że można przecież zainstalować małą ale bardzo szybką pamięć pomiędzy pamięcią RAM a procesorem.

Najtrudniejszym zadaniem komputera pozostaje wyselekcjonowanie, za pomocą 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 szybko odkryli, że 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, że 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.list

Teraz zaktualizuj APT i zainstaluj program

sudo apt-get update
sudo apt-get install redis

Odpal teraz serwer wywołując:

redis-server

Jeś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 ping

Redis Cli

redis cache

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, że 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 Toyota

Powyższa komenda zapisała w pamięci wartość 'Toyota’ (typu String) do klucza o nazwie 'car’. Jeśli chcę pobrać co przechowuje zmienna 'car’, wystarczy skorzystać z komendy GET. Każdy klucz jest unikalny, więc jeśli 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 10

Po 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śli klucz nie posiada żadnych danych, to zwróci none.

TYPE car

EXISTS

Czasami zamiast odczytywać wartość w mapie, chcesz po prostu sprawdzić czy ona istnieje. Komenda ta zwraca 1 gdy wartość istnieje, albo 1 jeśli nie.

EXISTS car

W moim przypadku rezultat na konsoli będzie:

(integer) 1

W 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 car

W 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 car

Wywołanie tej akcji zwróci 1, co oznacza prawidłowe usunięcie. Jeśli pomylisz się i spróbujesz usunąć nieistniejący klucz, to zwrócone zostanie 0.

RENAME

Możesz też podmienić nazwę klucza:

RENAME car brand

EXPIRE i TTL

Jeśli potrzebujesz dodać czas istnienia przechowywanych danych, możesz dodać też taką informację do istniejących kluczy.

EXPIRE brand 100

Po 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 brand

Gdy 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śli 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śli wszystkie zapytania są realizowane za pomocą Hibernate, a dana wartość została już raz pobrana, to zostanie ona zapamiętana. Natomiat jeśli 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śli 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śli 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 obecnie 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

Stay in the Loop

Get the daily email from CryptoNews that makes reading the news actually enjoyable. Join our mailing list to stay in the loop to stay informed, for free.

Ostatnio dodane

- Advertisement - spot_img

Powiązane wpisy