Code

Redis for Caching: How to Speed ​​Up Database Interactions

Redis for Caching: How to Speed ​​Up Database Interactions

Free Python course ➞ A mini-course for beginners and experienced coders. Four cool projects in the portfolio, live communication with the speaker. Click and see what you can learn in the course.

Learn more

Today we will develop a simple application that will interact with a MySQL database and implement a caching mechanism using Redis. All application components, including the database, will be deployed in Docker containers, which will ensure easy management and scalability. Using Docker will allow us to easily set up the environment and isolate dependencies, and Redis integration will help improve application performance by caching frequently accessed data.

If you are not yet familiar with Redis, we recommend starting with this article. It will cover the basic concepts and principles of working with this database management system. For information on working with Docker, follow this link. Redis is a powerful in-memory data store that provides fast query processing and high performance. Docker simplifies the process of deploying applications, including Redis, in isolated containers.

What application will we develop?

This Spring Boot application is designed to store books in a database and function as an online bookstore. It will allow users to easily find, browse, and purchase books through a user-friendly interface. The application will include functionality for managing the book catalog, adding new items, and processing orders. Thanks to Spring Boot, the application will have high performance and easy deployment. The main goal of the project is to create an effective platform for book lovers and book exchange, where everyone can find the literature they are interested in and make an online purchase.

Frequent database queries can significantly slow down applications. To improve performance, we use caching—a strategy that allows you to store query results in RAM. Thanks to this, when re-executing the same queries, the application speed increases significantly. Caching not only optimizes response times but also reduces database load, making applications more efficient and responsive.

If data is available in the cache, we use it from there. Otherwise, a more resource-intensive request is made to persistent storage. This approach optimizes system performance and speeds up access to information. Data caching significantly reduces database load and improves application responsiveness.

If you're new to Redis, we recommend starting with this article. It will give you a basic understanding of the capabilities and benefits of using Redis. To learn how to work with Docker, you can refer to the relevant material. Redis is a high-performance in-memory data store, ideal for caching, queuing, and many other scenarios. Docker, in turn, makes it easy to deploy and manage applications, including Redis, in containers, providing convenience and flexibility in development.

Preparation

  • Spring Web,
  • Spring Data JPA,
  • MySQL Driver,
  • Spring Data Redis
  • and Lombok (optional).

Download and unzip the resulting archive, then open it in your development environment. This will allow you to start working with the project and make the necessary changes. Make sure all files are correctly located to avoid problems when running the application.

Making the application

In the opened project, you need to create a directory structure that will ensure organized storage of files and simplified project management. To do this, follow the suggested directory structure, which will help you use resources efficiently and keep your code organized. The completed code is available for review.

Let's start the development process by creating a model, namely by defining the Book class, which represents an entity stored in the database. The Book class will include key attributes such as the book title, author, genre, publication date, and other important characteristics. A proper class structure will ensure efficient storage and management of book data, which forms the basis for further development of the application's functionality. It is also important to consider the possibility of expanding the model in the future, for example, by adding new fields or methods for working with data. This will make our system more flexible and adaptive to changing user requirements.

@Data is an annotation from the Lombok library that automatically generates boilerplate code for Java classes. It creates the necessary constructors, getters, and setters, as well as the equals(), hashCode(), and toString() methods. Using the @Data annotation significantly simplifies the process of writing code and makes it cleaner and more readable, freeing developers from the need to manually write monotonous methods. This annotation is especially useful for classes that act as data models. As a result, developers can focus on the business logic of the application without wasting time on routine tasks.

BookRepository is an interface that extends the capabilities of JpaRepository. Using Spring Data JPA eliminates the need to implement CRUD operations ourselves, simplifying development and increasing the efficiency of working with databases. This allows us to focus on the application's business logic, minimizing routine tasks associated with data processing.

The service class is a key element of the application architecture. It serves as an intermediate layer between the controller, which processes HTTP requests, and the repository, which is responsible for interacting with the database. Each method in the service class is annotated, ensuring efficient data caching. This significantly improves application performance by reducing the number of database queries and speeding up data processing for the user. Using this approach contributes to more structured and readable code, making it easier to maintain and scale.

The @CacheConfig annotation is used to configure all caching operations in the specified class. It allows for centralized management of cache parameters, such as the cache name, caching strategy, and other settings, which helps simplify caching in applications. Using @CacheConfig helps optimize application performance by reducing database load and improving system response times.

The @Cacheable annotation specifies that the result of a method is stored in the cache. Subsequent method calls retrieve the result from the cache using the key specified in the parameters. This optimizes application performance, reduces response times, and reduces the load on the database because the data is not requested repeatedly. Using @Cacheable allows for effective caching management and improves overall system performance.

The @CachePut annotation is used to update data in the cache. This annotated method ensures that the cached record is updated when it is executed, ensuring that the information is always current. Using @CachePut helps optimize application performance by minimizing database calls and reducing server load. Effective use of this annotation contributes to faster request processing and an improved overall user experience.

@CacheEvict is an annotation used in the Spring Framework to evict a record from the cache. This annotation allows you to manage data caching, ensuring that information in your application remains current. By using @CacheEvict, developers can remove specific entries from the cache when performing certain actions, such as updating or deleting data. This is especially useful for maintaining data consistency and optimizing application performance. Using @CacheEvict helps avoid situations where stale information remains in the cache, which can lead to errors or incorrect results.

The BookController class implements endpoints for all requests related to API development. This controller is responsible for processing requests, managing book data, and providing appropriate responses to clients. Optimizing the BookController ensures an efficient user experience and improves API performance.

The @GetMapping, @PostMapping, and @PutMapping annotations are used in Spring to indicate the invocation of the corresponding HTTP methods on the given path specified in the parameter. These annotations simplify the creation of RESTful web services, allowing developers to easily manage routes and process HTTP requests. @GetMapping is responsible for processing GET requests, @PostMapping is responsible for POST requests, and @PutMapping is responsible for PUT requests, making the code more readable and maintainable. Using these annotations allows you to effectively organize interactions between the client and the server in web applications.

Note the findAll() function, which is responsible for obtaining a list of all books. We measure the execution time of this operation and write the result to the log. This allows you to monitor performance and optimize database operations.

To enable caching in the application, you need to add the @EnableCaching annotation to the main class. This action will allow the system to run a post-processor that will process other annotations related to caching. Implementing this annotation is a key step in optimizing application operation and increasing its performance.

Deploying the Application and Databases

To efficiently deploy three services—MySQL, Redis, and the application—we will use Docker. This tool allows you to create isolated containers for each service, which ensures convenient management and scalability. During preparation, we will create Docker images and set up the necessary configurations for the correct operation of all components. This approach will ensure reliability and ease of deployment and management of services.

Creating a Dockerfile is an important step in the process of describing the image of your application. The Dockerfile contains the instructions necessary to build the image that will be used to run containers. In this file, you can specify the base image, install dependencies, configure the environment, and define the commands that will be executed when the container starts. A properly written Dockerfile will help streamline the process of deploying and managing your application, and ensure its compatibility with various environments. This will allow you to easily scale your application and keep it up to date.

Let's create a docker-compose file that describes several related containers. This file allows you to manage the configuration and launch of multiple services, simplifying the process of deploying and managing applications. Using docker-compose, you can quickly create, modify, and scale containers, as well as organize interactions between them. This is especially useful for developing and testing multi-service architectures. Properly configuring the docker-compose file can significantly increase the efficiency of working with containers and improve their integration within your project.

The file contains descriptions of three services, including container names, images, and port designations. The environment parameter is used to set environment variables, which allows you to customize the configuration of each service depending on its environment.

Configurations are made in the application.properties file. This file is key to configuring your application. You can configure various parameters, such as database connection settings, caching options, and other important properties that affect the operation of the application. Properly editing the application.properties file allows you to optimize the performance and reliability of your project. Be sure to test all changes before running the application to ensure its stable operation.

Note that the spring.datasource.url and spring.redis.host parameters must specify the container host addresses. This is important for the correct connection to the database and the Redis server in your application. Correctly configuring these parameters will help ensure the reliable operation of your application and efficient access to the necessary resources. Make sure that the container addresses are specified correctly to avoid connection issues.

The spring.cache.redis.time-to-live parameter determines the lifetime of data in the cache stored in Redis. This parameter allows you to effectively manage caching by setting the expiration date of cached objects. Setting the optimal time-to-live value helps reduce application response time and reduce the load on the database. Properly configuring this setting is important for maintaining data up-to-dateness and improving system performance.

Commands are used to perform this task. These commands allow you to effectively manage processes and automate actions. Using commands correctly significantly simplifies tasks and increases productivity. It's important to know which commands are available and how to use them to achieve the desired results. This will help you optimize your workflows and improve the quality of your tasks.

To build a project into a JAR file, use the command ./mvnw clean package -Dmaven.test.skip=true . This command cleans previous builds and creates a new JAR file, skipping tests. This approach can speed up the build process, especially in situations where testing is not a priority. Make sure you are in the root directory of your Maven project before running this command.

The docker-compose up command starts the process of executing the docker-compose.yml file, which includes building three images and creating the corresponding containers. This command is key to managing multi-container applications, allowing developers to easily deploy and scale their services. Proper use of docker-compose simplifies the development and testing process by providing a consistent environment for all application components.

The results will be displayed in the terminal.

Successful builds are indicated by the last four lines.

Testing the effect of caching

To test the functionality of the services, you need to execute a POST request that adds a new book several times. This process will help ensure that the API is working correctly and is able to handle requests for adding data. Regular testing of such requests allows you to identify possible errors and improve the stability of the service.

You can perform this operation either through the terminal or using Postman. Both tools allow you to conveniently interact with the API and execute requests. Using the terminal is suitable for developers who prefer the command line, while Postman provides a graphical interface, simplifying the process of testing and working with the API. Choose the method that's most convenient for you to solve the problem efficiently.

In the next terminal, run the specified commands.

Connecting to the Redis container allows us to verify that books added to the database are also successfully saved in the cache. To ensure the accuracy of the experiment, we clear the cache beforehand.

Connecting to the main application container to read logs is done using a special command. This process allows you to access information in the logs, which can be useful for diagnosing and monitoring the application. Make sure you have the necessary permissions to run this command. After a successful connection, you will be able to view and analyze the logs to identify potential problems or improve application performance.

The -f flag is used to read logs in real time, allowing you to display new entries as they appear in the console. A container ID such as 6f767bc19768 can be obtained using the docker ps command.

We then repeatedly execute the query to retrieve the list of books.

The logs display the following result:

The first query to the MySQL database took 676 milliseconds (Duration), indicating the time it took to retrieve the results. In contrast, the results of the two subsequent queries were retrieved from the cache, allowing them to execute 60 times faster. This highlights the importance of caching for optimizing database performance and reducing response times. Using a cache can significantly speed up access to frequently accessed data, which is especially important for improving the performance of web applications.

Retesting after clearing the cache confirms the results. Clearing the cache is an important procedure for ensuring the relevance of information and correcting possible errors. After completing this task, the test results show a significant improvement. This highlights the importance of regularly clearing the cache to maintain optimal site performance and functionality.

We successfully demonstrated the effectiveness of using Redis for data caching, significantly speeding up interactions with a relational database. As a high-performance in-memory data store, Redis significantly reduces response times and improves overall application performance. Implementing caching with Redis optimizes work with relational databases, which is especially important for projects with large volumes of requests and data.