Table of Contents
Overview
Redis is an in-memory data structure store that is often used as a database, cache, and message broker. There are several benefits to using Redis, including:
- Speed: Redis is very fast, as it stores data in memory instead of on disk. This makes it well-suited for applications that require low-latency access to large amounts of data.
- Flexibility: Redis supports a wide range of data structures, including strings, hashes, lists, sets, sorted sets, and more. This allows it to be used for a wide range of purposes, from simple key-value storage to complex data modeling.
- Scalability: Redis supports replication and sharding, which allows it to scale horizontally to support very large amounts of data and high levels of throughput.
- Multi-purpose: Redis can be used as a database, cache, and message broker, making it a versatile tool for many different types of applications.
In this post, we are going to discuss the different use cases of Redis in software systems.
Before you start, if you want to start a Redis instance quickly, here is the docker compose file:
version: '3' services: redis: image: redis:6.2.7-alpine ports: - "6379:6379"
Redis as a database
As a database, Redis can be used to store and retrieve large amounts of data in real time. It is handy for storing data that is frequently accessed and updated, as it can retrieve and update data much faster than a traditional disk-based database.
Redis as a database example in Java
package com.datmt.java.redis_example.db; import redis.clients.jedis.Jedis; public class RedisDatabase { private static final String KEY = "my-password"; private static final String MY_CAR_KEY = "my-car"; public static void main(String[] args) { // Create a new Jedis client Jedis jedis = new Jedis("localhost", 6379); // Set the value of the key jedis.set(KEY, "123456"); // Retrieve the value of the key String value = jedis.get(KEY); System.out.println("My password: " + value); jedis.del(KEY); //Try to get the key after deleted System.out.println("My password: " + jedis.get(KEY)); } }
Here, we create a new Jedis
client and use it to store, retrieve, and delete data from a Redis database. We use the set
and get
methods to store and retrieve the value of a key named my-password
.
We also use the del
method to delete the key and its associated value from the database.
The code above produces the following output:
This demonstrates the basic operations that can be performed on a Redis database using the Jedis
client. Redis also supports more advanced features such as transactions (we are going to try in the next section) and data structures, which can be used to implement more complex data storage and retrieval scenarios.
Redis transaction example in Java
package com.datmt.java.redis_example.db; import redis.clients.jedis.Jedis; import redis.clients.jedis.Transaction; public class RedisDatabaseTransaction { private static final String KEY_1 = "key-1"; private static final String KEY_2 = "key-2"; private static final String KEY_3 = "key-3"; private static final String KEY_4 = "key-4"; public static void main(String[] args) { happyTransaction(); try { failedTransaction(); } catch (Exception e) { // } var jedis = new Jedis("localhost", 6379) ; //try to get the data System.out.println("Key 1: " + jedis.get(KEY_1)); System.out.println("Key 2: " + jedis.get(KEY_2)); System.out.println("Key 3: " + jedis.get(KEY_3)); System.out.println("Key 4: " + jedis.get(KEY_4)); } private static void happyTransaction() { Jedis jedis = new Jedis("localhost", 6379); // Start a new transaction Transaction transaction = jedis.multi(); // Queue up some commands to be executed in the transaction transaction.set(KEY_1, "value-1"); transaction.set(KEY_2, "value-2"); // Execute the transaction transaction.exec(); } private static void failedTransaction () throws Exception { Jedis jedis = new Jedis("localhost", 6379); // Start a new transaction Transaction transaction = jedis.multi(); // Queue up some commands to be executed in the transaction transaction.set(KEY_3, "value-3"); transaction.set(KEY_4, "value-4"); //simulate bad things happen if (true) { throw new RuntimeException("Something bad happened"); } // Execute the transaction transaction.exec(); } }
The example above demonstrates the concept of transaction in Redis. We created two methods to simulate the happy case and failed case. The happy transaction completes without exception and the failed case has a run time exception.
Running the code above would produce the following output:
As you can see, we were able to set the value of KEY_1 and KEY_2. However, in the case of KEY_3 and KEY_4, the transaction was not committed due to a RuntimeException. Thus, there were no value is written to Redis.
Redis as a cache
As a cache, Redis can be used to store frequently accessed data in memory, allowing it to be retrieved quickly. This can improve the performance of a system by reducing the number of expensive database queries that need to be made.
Redis as a cache example in Java
Let’s implement a simple caching scenario using Redis in Java.
Consider there is a long calculation (digging digital coin for example). Someone need to use a lot of computation resources to calculate the hash:
private static String diggingBotCoin() { try { System.out.println("simulate long calculation"); Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } // Perform some computation to calculate the value return "Found the hash!!!!"; }
After the calculation, we want to store this value in the cache so we don’t have to wait another long period to get the value.
Here is a simple example to demonstrate the caching mechanism using Redis
private static void simpleCaching() { Jedis jedis = new Jedis("localhost", 6379); // Check if the value is in the cache if (jedis.exists(CACHE_KEY)) { // Retrieve the value from the cache String value = jedis.get(CACHE_KEY); System.out.println("Retrieved value from cache: " + value); } else { // Calculate the value String value = diggingBotCoin(); // Store the value in the cache jedis.set(CACHE_KEY, value); System.out.println("Stored value in cache: " + value); } }
When calling the function simpleCaching
, the value is not found in the cache. Thus, our program would run for about 5 seconds. However, the subsequent run returned almost instantly since the value is available in the Redis cache.
Using Redis as a cache allows us to efficiently retrieve frequently accessed data from the cache, improving the performance of our system. If the data changes, we can use the set
method to update the value in the cache, ensuring that the cache always contains the most up-to-date information.
Redis as a message broker
As a message broker, Redis can be used to enable communication between different components of a system. It can be used to implement a message queue, allowing parties to send and receive messages asynchronously. This can improve the scalability and reliability of a system, as components can continue to operate even if other components are unavailable or experiencing high levels of traffic.
Use Redis as a message broker in Java
Here is a quick example of using Redis as a message broker in Java.
First, we create a subscribe to a channel called sample-channel
. When messages is published to this channel, the subscriber will print out the messages.
package com.datmt.java.redis_example.broker; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPubSub; public class RedisBrokerSubscriber { private static final String CHANNEL_NAME = "sample-channel"; public static void main(String[] args) { // Create a new Jedis client Jedis jedis = new Jedis("localhost", 6379); // Subscribe to the channel jedis.subscribe(new MyMessageListener(), CHANNEL_NAME); } private static class MyMessageListener extends JedisPubSub { @Override public void onMessage(String channel, String message) { // Handle incoming message System.out.println("Received message: " + message); } } }
Next, let’s create a publisher to send messages to the same channel.
package com.datmt.java.redis_example.broker; import redis.clients.jedis.Jedis; public class RedisBrokerPublisher { private static final String CHANNEL_NAME = "sample-channel"; public static void main(String[] args) { // Create a new Jedis client Jedis jedis = new Jedis("localhost", 6379); // Subscribe to the channel jedis.publish(CHANNEL_NAME, "Hello, can you hear me?"); } }
Now, let’s run the consumer first. Next, run the publisher to send a message.
Switch to the consumer to see the message in the console:
Conclusion
Overall, Redis can play a crucial role in improving the performance, scalability, and reliability of a software system.
As usual, the code for this post is available here on Github
I build softwares that solve problems. I also love writing/documenting things I learn/want to learn.