Sai Vinesh
Sai VineshSenior Software Engineer

Redis i cache’owanie w Springu

Dowiedz się, czym jest Redis oraz jak możesz użyć go do cache’owania ze Spring Boot w Javie.
21.09.20217 min
Redis i cache’owanie w Springu

Cześć koderzy, ten artykuł skupia się głównie na Redis i na tym, dlaczego jest to idealna opcja jako baza danych do cache’owania dla Twojej aplikacji Spring Boot.



Jeśli instalujesz Redis na Windows, możesz użyć instrukcji z tego linku.

Czym jest Redis?


Redis to skrót od Re mote Di ctionary S erver i jest to open-source'owy magazyn danych przechowujących dane w strukturze klucz-wartość, który jest używany jako baza danych, pamięć podręczna i message broker. Redis jest rozwijany w technologii języka C, co czyni go niesamowicie szybkim. Używamy go, kiedy potrzebujemy szybkiej komunikacji pomiędzy serwerem a klientem.

Przechowuje on dane w pamięci i nie zapisuje ich na dysku, dzięki czemu jest taki szybki. Redis posiada jednak opcję zapisu danych na dysku i są one konfigurowalne. Może więc być używany jako system cache’owania lub jako w pełni rozwinięta baza danych.

Zalety Redis

  • Posiada jeden plik tekstowy, który zawiera pełną konfigurację.
  • Jest bardzo elastyczny, jeśli chodzi o przechowywanie danych i tak jak w przypadku wielu NoSQL, nie musimy definiować schematów, nazw kolumn, czy konkretnego typu danych dla każdej wartości.
  • Jest on wyjątkowo szybki i może wykonać około 110 000 SET-ów na sekundę, i około 81 000 GET-ów na sekundę.
  • Jest on jednowątkowy i dlatego wykonuje tylko jedną akcję w danym momencie, co pomaga nam uniknąć problemów ze współbieżnością.
  • W Redisie istnieje funkcja pipeline, dzięki której możemy klasteryzować wiele poleceń i wysyłać w tym samym czasie, dzięki czemu Redis staje jest jeszcze szybszy.


Redis jest bazą danych No-SQL, więc nie posiada żadnych tabel, wierszy ani kolumn, a także nie pozwala na żadne instrukcje typu SELECT, INSERT, UPDATE lub DELETE. Zamiast tego Redis używa typów danych do ich przechowywania.

5 podstawowych typów danych w Redis:

  1. String
  2. List
  3. Set
  4. Sorted set
  5. Hash


Redis posiada również 3 dodatkowe typy danych: Bitmapy, Hiperlogi i Indeksy geoprzestrzenne.

Redis ma również funkcję replikacji danych master-slave, w której możemy używać różnych instancji Redisa i możemy uczynić dowolną z nich masterem, a inne slave’ami, co bardzo pomaga w skalowaniu. Replikacja jest wykonywana asynchronicznie, co oznacza, że master będzie kontynuował obsługę zapytań, gdy slave'y będą się synchronizować.

Konfiguracja projektu Spring Boot

Użyłem Spring Initializr do stworzenia podstawowego Projektu Spring Boot i dodałem następujące zależności:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <scope>runtime</scope>
</dependency>


Używam MySQL jako mojej głównej bazy danych, więc stosuję tutaj konektor MySQL. Dodałem również zależność do obsługi Redisa w Springu, która jest używana jako klient Redisa w naszym projekcie, co pozwala naszej aplikacji włączyć mechanizm cache’owania w Redis.

@Entity
public class User implements Serializable{
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private Integer id;
	private String name;
	private String email;
	private Integer age;
	public User() {
		super();
	}
	
	public User(Integer id, String name, String email, Integer age) {
		super();
		this.id = id;
		this.name = name;
		this.email = email;
		this.age = age;
	}
}


Dla encji, musimy zaimplementować serializację, ponieważ dane będą przechowywane jako strumień bajtów w Redisie, a to pomaga w serializacji i deserializacji danych.

Adnotacje cache’owania

Powszechne adnotacje używane podczas implementacji cache’owania są następujące @CachePut, @CacheEvict, @Cacheable, @Caching. Zobaczmy, gdzie i jak możemy ich użyć.

@SpringBootApplication
@EnableCaching
public class RedisCacheApplication {

	public static void main(String[] args) {
		SpringApplication.run(RedisCacheApplication.class, args);
	}
}


W naszej głównej klasie aplikacji musimy dodać @EnableCaching, co pozwoli Springowi dowiedzieć się, że w tej aplikacji cache’owanie jest włączone.

Spring Boot automatycznie konfiguruje infrastrukturę pamięci podręcznej tak długo, jak długo obsługa cache’owania jest włączona za pomocą adnotacji @EnableCaching.

W kontrolerze musimy dodać różne adnotacje na każdym API, aby cache’ować konkretny rekord danych.

@GetMapping("/{id}")
@Cacheable(value= "Users", key="#id")
public User getUser(@PathVariable Integer id) {
	log.info(">> User Controller: get user by id: " + id);
	return userService.getUserById(id);
}


Na górze metody GET używamy @Cacheable, co oznacza, że za każdym razem, gdy metoda jest wywoływana, zostanie zastosowane cache’owanie sprawdzające, czy metoda była już wywoływana dla podanych argumentów.

value="{category}" określa, pod jaką kategorią mają być przechowywane dane. Dla różnych kontrolerów możemy używać różnych wartości.

Rozsądnym rozwiązaniem domyślnym jest po prostu użycie parametrów metody do obliczenia klucza, ale możemy również określić klucz używając key="#{value}", który będzie użyty do identyfikacji konkretnego rekordu.

Jeżeli dla obliczonego klucza lub podanego klucza nie zostanie znaleziona wartość w pamięci podręcznej, to zostanie wywołana metoda docelowa, a zwrócona wartość zostanie zapisana w powiązanej pamięci podręcznej. Typy zwracane są obsługiwane automatycznie, a ich zawartość jest przechowywana w pamięci podręcznej, jeśli taka występuje.

@PutMapping
@CachePut(value="users", key="#user", unless="#result == null")
public User updateUser(@RequestBody User user) {
	log.info(">> User Controller: update user: " + user.toString());
	return userService.update(user);
}


Na szczycie metody PUT używamy @CachePut. Do zapisania nowego użytkownika możemy użyć również samego @CachePut, czyli metody POST. W przeciwieństwie do adnotacji @Cacheable, ta adnotacja nie powoduje, że metoda jest pomijana. Raczej zawsze powoduje wywołanie metody i zapisanie jej wyniku w powiązanej z nią pamięci podręcznej, jeśli wyrażenia #condition() i #unless() zostały spełnione.

Condition() jest wyrażeniem Spring Expression Language (SpEL), służącym do warunkowego wykonania operacji umieszczenia w pamięci podręcznej. To wyrażenie jest obliczane po wywołaniu metody ze względu na naturę operacji put i dlatego może odnosić się do wyniku metody.

Unless() jest wyrażeniem Spring Expression Language (SpEL) używanym do zawetowania operacji umieszczania w pamięci podręcznej.

@DeleteMapping("/{id}")
@CacheEvict(value= "Users", allEntries = false, key="#id")
public void removeUser(@PathVariable Integer id) {
  log.info(">> User Controller: delete user: " + id);
  userService.delete(id);
}


Na szczycie metody DELETE, używamy @CacheEvict, która pozwala nam na usunięcie danych dla konkretnego klucza w Redis. allEntries określa, czy wszystkie wpisy wewnątrz pamięci podręcznej powinny zostać usunięte. Domyślnie usuwana jest tylko wartość pod powiązanym kluczem. Zauważ, że ustawienie tego parametru na wartość true i podanie klucza nie jest dozwolone.

Właściwości aplikacji 

Możemy skonfigurować cache’owanie Redisa w naszej aplikacji poprzez różne właściwości. Ja użyłem poniższych:

spring.redis.host=localhost
spring.redis.port=6379
spring.cache.redis.time-to-live=60000
spring.cache.cache-names=users,product,order


Domyślnie pamięci podręczne są tworzone w miarę zapotrzebowania, ale można ograniczyć listę dostępnych pamięci podręcznych poprzez ustawienie właściwości cache-names

Właściwości, których możemy użyć do skonfigurowania cache’owania Redis w Spring, są takie jak poniżej:

Jak Redis przechowuje dane

Możemy użyć redis-cli, aby zobaczyć, jak dane są przechowywane w Redis. Wygląda to tak, jak na poniższym obrazku. Dane są przechowywane w strumieniu bajtów i dlatego zaimplementowaliśmy  Serializable dla klasy encji, co pomaga w konwersji strumienia obiektów do rzeczywistego obiektu Javy, który ma być użyty w naszym programie.

kliknij kod, aby powiększyć


Ważne do zapamiętania:

  • Określ TTL :Time-to-live (TTL) to okres, po którym Twoja pamięć podręczna będzie usuwała wpis. Jeśli chcesz pobierać dane tylko raz na minutę, po prostu wymuś to za pomocą adnotacji @Cacheable i ustaw TTL na 1 minutę.
  • Zaimplementuj serializację:Jeśli dodajemy obiekt do pamięci podręcznej Redis, to obiekt ten powinien implementować interfejs Serializable.
  • Limity pamięci podręcznej Redis:Kiedy rozmiar pamięci podręcznej osiągnie limit, stare dane są usuwane, aby ustąpić miejsca nowym. Mimo że Redis jest bardzo szybki, nie ma ograniczeń w przechowywaniu dowolnej ilości danych w systemie 64-bitowym. W systemie 32-bitowym może przechowywać tylko 3GB danych.
  • Nigdy nie wywołuj metody z Cacheable z tej samej klasy:Powodem jest to, że Spring pośredniczy w dostępie do tych metod, aby abstrakcja pamięci podręcznej działała. Kiedy wywołujesz go w obrębie tej samej klasy, mechanizm Proxy nie działa. W ten sposób w zasadzie pomijasz pamięć podręczną i sprawiasz, że staje się ona nieefektywna.

Podsumowanie

Artykuł ten podsumowuje podstawy Redisa i to, jak użyć go jako cache w Spring Boot. Podrzucam link do repo na GitHub z przykładem aplikacji z takim cache.

<p>Loading...</p>