Redis stands for REmote DIctionary Server. It is an in-memory key-value database server. In-memory means that redis using memory to store data instead of disks, which makes redis super fast for reading and writing data. Since redis is a key-value database server, it is a no-SQL server. It supports data structures such as strings, lists, sets, sorted sets, hashes, HyperLogLogs, maps, bitmaps, streams, and spatial indexes. For more information about redis, please visit official website.
Two most important reasons:
Besides the above two reasons, there are some other reasons why we want to use redis, such as well documented, simple to use and scalable. Redis is most commonly used as a cache server. So the next question is what cache is?
A cache server is a dedicated network server or service acting as a server that saves Web pages or other Internet request/content locally. By placing previously requested information in temporary storage, or cache, a cache server both speeds up access to data and reduces demand on bandwidth and repeatable computing cost.
In a word, cache can increase system performance, such as:
Based on how the data is read and write in the server, there are different cache strategies to choose:
This article gives more details about each strategy and when to choose it. I am not going to repeat here.
For demo purpose, we run a redis server locally in docker with the following two commands:
docker pull redis
docker run -p 6379:6379 --name some-redis redis
When the redis server is build successfully, you can access the server by opening another terminal and typing the following command:
docker exec -it some-redis /bin/bash
Once we access the server sucessfull, we can use the following commands:
redis-cli keys * (check all current keys in redis database)
redis-cli FLUSHDB (clean redis database)
redis-cli info stats | grep 'keyspace_*' (check miss and hit, which will be explained later)
Once the redis server is ready, we can integrate it with SpringBoot application. PostgreSQL database is used in this demo. Before you follow the following steps, you should make sure your SpringBoot can access your database through controllers.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
#redis config
spring.cache.type=redis
spring.redis.host=localhost
spring.redis.port=6379
For a read-through cache, add:
@Cacheable(value = "departments")
For a write-through cache, add:
@CachePut(value = "departments", key = "#department.id", unless = “#department.name==null”)
@Entity
@Table(name="departments")
public class Department implements Serializable {
//...
}
Or other wise you may get the following error:
Request processing failed;
nested exception is org.springframework.data.redis.serializer.SerializationException:Cannot deserialize;
nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to deserialize payload. Is the byte array a result of corresponding serialization for DefaultDeserializer?;
nested exception is java.io.InvalidClassException: com.ascending.training.model.Department;
class invalid for deserialization
Step 1: To integrate a Read-Through Cache, we add @Cacheable(value = “departments”) in front of GET methods in the DepartmentController. As we metioned above, redis is a key-value server. Here “departments” in the bracket is a string for the value part.
@Cacheable(value = "departments")
@GetMapping(value = "/{deptName}", produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
public Department getDepartmentByName(@PathVariable(name = "deptName") String deptName1){
return departmentService.getDepartmentByName(deptName1);
}
Step 2: start a redis server in docker container, and run docker exec -it some-redis /bin/bash to get into the redis server. Run redis-cli keys * and find that there is no key in it. Run redis-cli info stats | grep ‘keyspace_*’ and get
keyspace_hits:0
keyspace_misses:0
Step 3: start SpringBoot and then in postman submit a GET request to get department by department name “AOES” as usual.
Step 4: run redis-cli info stats | grep ‘keyspace_*’ and get
keyspace_hits:0
keyspace_misses:1
because redis server is empty so it misses the first request from postman. Run redis-cli keys * and this time we find that “value::key” pair shown as
1) "departments::AOES"
where “departments” is the value we defined in the controller and “AOES” is the department name we requested. If you send the same request again by postman and run redis-cli info stats | grep ‘keyspace_*’, you will get
keyspace_hits:1
keyspace_misses:1
because after the first request redis has stored the data in the database and for the second request, postman gets the data from the redis server successfully (1 hit). If you check the server end output from the IntelliJ IDE, you will find no Hibernate output, which proves that the second request gets data from cache instead of database.
Also note that the second time when we read data from redis is way faster than the first time we read data from database. Using an App’s health diagnose tool Spring Boot Actuator, I compared the time difference. It takes about 457 ms reading data from database, while only around 100 ms from redis server.
Step 1: Add @CachePut(value = “departments”, key = “#department.id”, unless = “#department.name==null”) in front of POST methods for a Write-Through Cache. For the key parameter, we use “#department.id”, where “department” is our Java object name, “#” sign means “department” is not a string but a variable. “unless” will exclude some conditions. In this case, cache will not store the record when the department name is null.
@CachePut(value = "departments", key = "#department.id", unless = "#department.name == null")
@PostMapping(value = "", consumes = {MediaType.APPLICATION_JSON_VALUE})
public String creatDepartment(@RequestBody Department department){
boolean isSuccess = departmentService.save(department);
return isSuccess?"Success":"Fail";
}
Step 2: Similar to the read-through cache example, at the beginning, there is no keys stored in the server and 0 misses/0 hits.
Step 3: start SpringBoot and then in postman submit a POST request to create a new department { “id”:5, “name”: “MS”,”location”:”Exploring Hall”,”description”:”Mathematical Sciences”}.
Step 4: run redis-cli info stats | grep ‘keyspace_*’ and still get
keyspace_hits:0
keyspace_misses:0
However if we run redis-cli keys * and this time we find that “value::key” pair shown as
1) "departments::5"
where “departments” is the value we defined in the controller and “5” is the id of the department we just created. The write-through cache worked!
The author would like to thank Ryo for his awesome redis lecture, and also thank him for reviewing and editing an early version of this article and for many constrictive suggestions.
His github: https://github.com/ascending-llc/spring-redis
Have fun with redis!
Github: https://github.com/lnsdlszsqxxx/runtime-ii