How to Optimize Your Backend Application with Redis Cache
Tags: redis, java, backend, cache
Published: 2023-09-08 09:33:39

## Introduction

In this repository, we will explore the use of cache with Spring and provide a brief introduction to Redis in a straightforward manner. We will present examples using Spring, but you can adapt these concepts to other web frameworks.

### Code Repository

- [Cache-spring](https://github.com/JeanV0/uber-integration/tree/feat/cache)

### What Is Cache?

Cache is a mechanism used to temporarily store frequently accessed data, such as static components of a website (e.g., images and documents). This technique speeds up data access by reducing the need for repetitive queries to slower sources like databases.

"Cache is the term used to classify a specific set of saved information that reflects static components of the site, such as images and general documents that make up the page." - [CanalTech.](https://www.techtudo.com.br/listas/2020/07/o-que-e-cache-no-navegador-entenda-para-que-serve-e-como-limpar.ghtml#:~:text=O%20que%20%C3%A9%20cache%20e%20para%20que%20serve%3F,n%C3%A3o%20precisa%20requisitar%20todos%20os%20dados%20da%20)

### Why Use Cache?

Typically, data is stored directly in a database. SQL or relational databases use disk or SSD storage, which involves limited read and write speeds. CPU, on the other hand, is costlier for local storage and often slower. In many cases, databases can optimize performance by caching a portion or even the entire database in RAM, resulting in a significant overall performance improvement.

However, there are ways to optimize further, especially when dealing with NoSQL databases like MongoDB, Redis, DynamoDB, and others, which focus on a key-value database model or exclusive use of RAM. These NoSQL databases are known for their speed, although they are considered less reliable. Optimization can be achieved by storing specific queries in Redis and retrieving them from there instead of accessing the SQL database. This results in time savings, reduced CPU usage, and resource conservation.

### What Is Redis?

Typically, data is stored directly in a database. SQL or relational databases use disk or SSD storage, which involves limited read and write speeds. CPU, on the other hand, is costlier for local storage and often slower. In many cases, databases can optimize performance by caching a portion or even the entire database in RAM, resulting in a significant overall performance improvement.

However, there are ways to optimize further, especially when dealing with NoSQL databases like MongoDB, Redis, DynamoDB, and others, which focus on a key-value database model or exclusive use of RAM. These NoSQL databases are known for their speed, although they are considered less reliable. Optimization can be achieved by storing specific queries in Redis and retrieving them from there instead of accessing the SQL database. This results in time savings, reduced CPU usage, and resource conservation.

### What Can Be Cached?

Cache can be used to store and accelerate access to various types of data and resources in an application. Here are some things that can benefit from cache usage:

1. Database Data: One of the most common use cases is caching the results of database queries. This can include objects, SQL queries, or even dynamically generated HTML pages.
2. Static Resources: Static elements such as images, CSS stylesheets, JavaScript scripts, and media files can be cached to reduce server load and speed up web page loading.
3. Results of Lengthy Computations: If a computationally intensive operation is repeatedly requested with the same parameters, the results can be cached to avoid the need for recalculations each time.
4. Session Information: Session data can be cached to avoid frequent queries to the server's session storage, resulting in faster responses.
5. APIs and External Requests: If an application makes calls to external APIs or web services, the results of these calls can be cached to reduce the overhead of repeated calls and improve latency.

Effectively using cache involves identifying which parts of your application can benefit from caching and implementing an appropriate caching strategy for these elements. It's essential to manage the cache carefully to ensure that cached data is updated when needed and that excessive cache usage does not lead to data integrity problems.

### Example

In this case, we'll use a Java code example that directly utilizes Redis and Spring's cache support. Both are simplified for illustrative purposes. Initially, we won't mention libraries or installation requirements, so no need to worry about that. We'll also use the MVC pattern. Let's dive right into the service.

This code illustrates how you can implement caching using Spring and Redis to efficiently store and retrieve driver data.

```java
@Cacheable("Drivers")
public List<Driver> getDriver() {
    return this.repository.findAll();
}

@Scheduled(cron = "10 * * * * *")
@CacheEvict("Drivers")
public void clearCache() {
    System.out.println("Clear cache drivers");
}

public List<Driver> getDriverByRedis() throws JsonProcessingException {
    String value = this.redisService.getKeyValue("Drivers_Redis");
    if (value == null || value == "") {
        List<Driver> drivers = this.repository.findAll();
        this.redisService.saveKeyValue("Drivers_Redis", objectMapper.writeValueAsString(drivers));
        return drivers;
    }
    return objectMapper.readValue(value, new TypeReference<List<Driver>>() {});

}
```

In this case, some refer to it as a cache pool or key, but here we'll stick with the term "key." When someone invokes the getDriver function to retrieve drivers, if the cache doesn't exist, it will execute and save the data in "Drivers." This eliminates the need to directly query the database.

However, sometimes data changes, or we need to refresh it occasionally. Spring provides support using CacheEvict. When you call the method, it clears the value associated with the key, forcing the next call to getDriver to recreate the value. This can be useful when an entity has been altered, among other scenarios.

In some cases, there's no framework support for cache creation. Therefore, we have a purer example here of how to create a cache. In this last case, I haven't provided code for clearing cache at intervals, as I didn't have much time. Redis operates with key-value pairs; if the value doesn't exist, it can return null. That's why we perform an if check to see if it's null or empty. It's essential to note that this example is simplified and may not be suitable for complex systems, but it serves an illustrative purpose.

### Pros and Cons

Yes, implementing cache can either enhance or diminish the security and performance of your application. I'll highlight some details, focusing on Redis and caching in general.

#### Pros

1. Improved Performance: Cache speeds up access to frequently used data, reducing the need for time-consuming queries to data sources like databases.
2. Reduced Latency: Cached data resides in memory, making access much faster compared to querying databases on disk.
3. Scalability: Redis is highly scalable, making it an effective solution for high-traffic applications. You can easily distribute the cache across multiple servers.
4. Database Load Reduction: Cache reduces the load on the database, relieving it from frequent and heavy queries, which can help avoid overload issues.
5. Resource Savings: Cache conserves system resources like CPU and I/O since it reduces the need for frequent database accesses.
6. Enhanced User Experience: With faster response times, users experience more responsive application performance, leading to an improved user experience.

#### Cons

1. Data Consistency: Cache can introduce data consistency issues because cached data may be outdated compared to the data in the underlying database. This requires careful cache management strategies, such as setting appropriate expiration times or cache invalidation when data changes. Cached data is not directly related to the database without intermediaries.
2. Additional Complexity: Implementing and managing a cache system adds complexity to application development. You need to ensure that cached data is updated when necessary.
3. Memory Requirements: In-memory cache like Redis consumes RAM. This means you need to allocate enough memory to handle your cached data, which can increase infrastructure costs.
4. Potential Data Leaks: Without proper cache management, there can be leaks of sensitive information as cached data may be accessed by unauthorized parties.
5. Implementation Cost: Setting up and maintaining a cache system, especially if it involves dedicated servers like Redis, can increase infrastructure and maintenance costs.
6. Cache Invalidation Complexity: Cache invalidation can be complex and requires extra care to ensure that the cache is updated correctly when underlying data changes.
7. Cache Misses: In some cases, if the cache is not designed or configured correctly, there can be a high rate of "cache misses," which means that data is not cached and needs to be fetched from the database, negating the benefits of caching.

In summary, using cache, including Redis, can be a powerful strategy to improve the performance and scalability of your application, but it also requires careful consideration and management to avoid data consistency issues and leaks of sensitive information. Each application is unique, so the decision to use cache should be based on the specific needs of your project and an understanding of the pros and cons associated with caching.

I hope you found this helpful and informative!

My social media profiles:

- Linkedin: [JeanVictor1](https://www.linkedin.com/in/jeanvictor1/)
- Dev.to: [@mrjeandev](https://dev.to/mrjeandev)