I just announced the new Spring Boot 2 material, coming in REST With Spring:

>> CHECK OUT THE COURSE

1. Overview

This article is an introduction to Jedis, a client library in Java for Redis – the popular in-memory data structure store that can persist on disk as well. It is driven by a keystore-based data structure to persist data and can be used as a database, cache, message broker, etc.

First, we are going to explain in which kind of situations Jedis is useful and what it is about.

In the subsequent sections we are elaborating on the various data structures and explaining transactions, pipelining and the publish/subscribe feature. We conclude with connection pooling and Redis Cluster.

2. Why Jedis?

Redis lists the most well-known client libraries on their official site. There are multiple alternatives to Jedis, but only two more are currently worthy of their recommendation star, lettuce, and Redisson.

These two clients do have some unique features like thread safety, transparent reconnection handling and an asynchronous API, all features of which Jedis lacks.

However, it is small and considerably faster than the other two. Besides, it is the client library of choice of the Spring Framework developers, and it has the biggest community of all three.

3. Maven Dependencies

Let’s start by declaring the only dependency we will need in the pom.xml:

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.8.1</version>
</dependency>

If you’re looking for the latest version of the library, check out this page.

4. Redis Installation

You will need to install and fire up one of the latest versions of Redis. We are running the latest stable version at this moment (3.2.1), but any post 3.x version should be okay.

Find here more information about Redis for Linux and Macintosh, they have very similar basic installation steps. Windows is not officially supported, but this port is well maintained.

After that we can directly dive in and connect to it from our Java code:

Jedis jedis = new Jedis();

The default constructor will work just fine unless you have started the service on a non-default port or a remote machine, in which case you can configure it correctly by passing the correct values as parameters into the constructor.

5. Redis Data Structures

Most of the native operation commands are supported and, conveniently enough, they normally share the same method name.

5.1. Strings

Strings are the most basic kind of Redis value, useful for when you need to persist simple key-value data types:

jedis.set("events/city/rome", "32,15,223,828");
String cachedResponse = jedis.get("events/city/rome");

The variable cachedResponse will hold the value 32,15,223,828. Coupled with expiration support, discussed later, it can work as a lightning fast and simple to use cache layer for HTTP requests received at your web application and other caching requirements.

5.2. Lists

Redis Lists are simply lists of strings, sorted by insertion order and make it an ideal tool to implement, for instance, message queues:

jedis.lpush("queue#tasks", "firstTask");
jedis.lpush("queue#tasks", "secondTask");

String task = jedis.rpop("queue#tasks");

The variable task will hold the value firstTask. Remember that you can serialize any object and persist it as a string, so messages in the queue can carry more complex data when required.

5.3. Sets

Redis Sets are an unordered collection of Strings that come in handy when you want to exclude repeated members:

jedis.sadd("nicknames", "nickname#1");
jedis.sadd("nicknames", "nickname#2");
jedis.sadd("nicknames", "nickname#1");

Set<String> nicknames = jedis.smembers("nicknames");
boolean exists = jedis.sismember("nicknames", "nickname#1");

The Java Set nicknames will have a size of 2, the second addition of nickname#1 was ignored. Also, the exists variable will have a value of true, the method sismember enables you to check for the existence of a particular member quickly.

5.4. Hashes

Redis Hashes are mapping between String fields and String values:

jedis.hset("user#1", "name", "Peter");
jedis.hset("user#1", "job", "politician");
		
String name = jedis.hget("user#1", "name");
		
Map<String, String> fields = jedis.hgetAll("user#1");
String job = fields.get("job");

As you can see, hashes are a very convenient data type when you want to access object’s properties individually since you do not need to retrieve the whole object.

5.5. Sorted Sets

Sorted Sets are like a Set where each member has an associated ranking, that is used for sorting them:

Map<String, Double> scores = new HashMap<>();

scores.put("PlayerOne", 3000.0);
scores.put("PlayerTwo", 1500.0);
scores.put("PlayerThree", 8200.0);

scores.keySet().forEach(player -> {
    jedis.zadd("ranking", scores.get(player), player);
});
		
String player = jedis.zrevrange("ranking", 0, 1).iterator().next();
long rank = jedis.zrevrank("ranking", "PlayerOne");

The variable player will hold the value PlayerThree because we are retrieving the top 1 player and he is the one with the highest score. The rank variable will have a value of 1 because PlayerOne is the second in the ranking and the ranking is zero-based.

6. Transactions

Transactions guarantee atomicity and thread safety operations, which means that requests from other clients will never be handled concurrently during Redis transactions:

String friendsPrefix = "friends#";
String userOneId = "4352523";
String userTwoId = "5552321";

Transaction t = jedis.multi();
t.sadd(friendsPrefix + userOneId, userTwoId);
t.sadd(friendsPrefix + userTwoId, userOneId);
t.exec();

You can even make a transaction success dependent on a specific key by “watching” it right before you instantiate your Transaction:

jedis.watch("friends#deleted#" + userOneId);

If the value of that key changes before the transaction is executed, the transaction will not be completed successfully.

7. Pipelining

When we have to send multiple commands, we can pack them together in one request and save connection overhead by using pipelines, it is essentially a network optimization. As long as the operations are mutually independent, we can take advantage of this technique:

String userOneId = "4352523";
String userTwoId = "4849888";

Pipeline p = jedis.pipelined();
p.sadd("searched#" + userOneId, "paris");
p.zadd("ranking", 126, userOneId);
p.zadd("ranking", 325, userTwoId);
Response<Boolean> pipeExists = p.sismember("searched#" + userOneId, "paris");
Response<Set<String>> pipeRanking = p.zrange("ranking", 0, -1);
p.sync();

String exists = pipeExists.get();
Set<String> ranking = pipeRanking.get();

Notice we do not get direct access to the command responses, instead, we’re given a Response instance from which we can request the underlying response after the pipeline has been synced.

8. Publish/Subscribe

We can use the Redis messaging broker functionality to send messages between the different components of our system. Make sure the subscriber and publisher threads do not share the same Jedis connection.

8.1. Subscriber

Subscribe and listen to messages sent to a channel:

Jedis jSubscriber = new Jedis();
jSubscriber.subscribe(new JedisPubSub() {
    @Override
    public void onMessage(String channel, String message) {
        // handle message
    }
}, "channel");

Subscribe is a blocking method, you will need to unsubscribe from the JedisPubSub explicitly. We have overridden the onMessage method but there are many more useful methods available to override.

8.2. Publisher

Then simply send messages to that same channel from the publisher’s thread:

Jedis jPublisher = new Jedis();
jPublisher.publish("channel", "test message");

9. Connection Pooling

It is important to know that the way we have been dealing with our Jedis instance is naive. In a real-world scenario, you do not want to use a single instance in a multi-threaded environment as a single instance is not thread-safe.

Luckily enough we can easily create a pool of connections to Redis for us to reuse on demand, a pool that is thread safe and reliable as long as you return the resource to the pool when you are done with it.

Let’s create the JedisPool:

final JedisPoolConfig poolConfig = buildPoolConfig();
JedisPool jedisPool = new JedisPool(poolConfig, "localhost");

private JedisPoolConfig buildPoolConfig() {
    final JedisPoolConfig poolConfig = new JedisPoolConfig();
    poolConfig.setMaxTotal(128);
    poolConfig.setMaxIdle(128);
    poolConfig.setMinIdle(16);
    poolConfig.setTestOnBorrow(true);
    poolConfig.setTestOnReturn(true);
    poolConfig.setTestWhileIdle(true);
    poolConfig.setMinEvictableIdleTimeMillis(Duration.ofSeconds(60).toMillis());
    poolConfig.setTimeBetweenEvictionRunsMillis(Duration.ofSeconds(30).toMillis());
    poolConfig.setNumTestsPerEvictionRun(3);
    poolConfig.setBlockWhenExhausted(true);
    return poolConfig;
}

Since the pool instance is thread safe, you can store it somewhere statically but you should take care of destroying the pool to avoid leaks when the application is being shutdown.

Now we can make use of our pool from anywhere in the application when needed:

try (Jedis jedis = jedisPool.getResource()) {
    // do operations with jedis resource
}

We used the Java try-with-resources statement to avoid having to manually close the Jedis resource, but if you cannot use this statement you can also close the resource manually in the finally clause.

Make sure you use a pool like we have described in your application if you do not want to face nasty multi-threading issues. You can obviously play with the pool configuration parameters to adapt it to the best setup in your system.

10. Redis Cluster

This Redis implementation provides easy scalability and high availability, we encourage you to read their official specification if you are not familiar with it. We will not cover Redis cluster setup since that is a bit out of the scope for this article, but you should have no problems in doing so when you are done with its documentation.

Once we have that ready, we can start using it from our application:

try (JedisCluster jedisCluster = new JedisCluster(new HostAndPort("localhost", 6379))) {
    // use the jedisCluster resource as if it was a normal Jedis resource
} catch (IOException e) {}

We only need to provide the host and port details from one of our master instances, it will auto-discover the rest of the instances in the cluster.

This is certainly a very powerful feature but it is not a silver bullet. When using Redis Cluster you cannot perform transactions nor use pipelines, two important features on which many applications rely for ensuring data integrity.

Transactions are disabled because, in a clustered environment, keys will be persisted across multiple instances. Operation atomicity and thread safety cannot be guaranteed for operations that involve command execution in different instances.

Some advanced key creation strategies will ensure that data that is interesting for you to be persisted in the same instance will get persisted that way. In theory, that should enable you to perform transactions successfully using one of the underlying Jedis instances of the Redis Cluster.

Unfortunately, currently you cannot find out in which Redis instance a particular key is saved using Jedis (which is actually supported natively by Redis), so you do not know which of the instances you must perform the transaction operation. If you are interested about this, you can find more information here.

11. Conclusion

The vast majority of the features from Redis are already available in Jedis and its development moves forward at a good pace.

It gives you the ability to integrate a powerful in-memory storage engine in your application with very little hassle, just do not forget to set up connection pooling to avoid thread safety issues.

You can find code samples in the GitHub project.

I just announced the new Spring Boot 2 material, coming in REST With Spring:

>> CHECK OUT THE LESSONS

newest oldest most voted
Notify of
Nikita Koksharov
Guest
Nikita Koksharov

Could you also make overview of Redisson framework (http://redisson.org) ?

Eugen Paraschiv
Guest

That’s an interesting client – thanks for the suggestion. I had a look and the only problem is that the open source version is very limited (not realistic to use it for anything serious). And I generally only cover open source stuff here – hope that makes sense.
Cheers,
Eugen.

Nikita Koksharov
Guest
Nikita Koksharov

> I had a look and the only problem is that the open source version is very limited

What do you mean limited? limited in what? Redisson covers almost all Redis commands

> not realistic to use it for anything serious

Could you please give me an example? Because open-source version used by many companies around the world

Eugen Paraschiv
Guest

Hey Nikita – I’m looking at the standard vs Pro version and I see – for example – that the cluster related features are only available in the Pro version (cluster management, data partitioning). Now – single node is fine, but as soon as you go to a clustered setup (which is most setups) – it looks like you need to buy the pro version.
I’ve only looked at it briefly, so let me know if that’s not the case.
Cheers,
Eugen.

Nikita Koksharov
Guest
Nikita Koksharov

@baeldung:disqus Open-source version works in clustered setup too. As of cluster management feature, for now you have to use `./redis-trib.rb` to manage redis cluster. Redisson PRO offers special Cluster Management API which fully replaces this ruby script. This is all the feature about. Open-source version fully supports work in cluster mode as stated on site. If you need to use single map partitioned (this feature not available in Redis itself or jedis) to all master nodes of Redis cluster. When you need to buy pro version but this does not prohibit you to create single map and make key partitioning… Read more »

Eugen Paraschiv
Guest

I see – then my assumption was wrong – thank for clearing that up.
I’m adding the topic to the internal content calendar.
Cheers,
Eugen.

Nikita Koksharov
Guest
Nikita Koksharov

Thanks, I’m appreciate it! In case you have any more questions feel free to email me – [email protected]

Eugen Paraschiv
Guest

Sure thing Nikita, thanks for clearing everything up. Cheers,
Eugen.

Rui Gu
Guest
Rui Gu

Hi Eugen, I think you might have confused by the naming of those Pro features. Data partitioning of the pro feature is a way to shard a single collection across multiple Redis nodes, which is not natively supported by Redis. Standard data distribution brought to us by Redis Cluster is supported in Redisson as a first class feature which is available to all in OS edition. As for “Cluster Management” feature, the key part is the management, it gave the user the ability to manage cluster within application’s logic instead of relying on the official tribe tool which is a… Read more »

JC
Guest
JC

How does Jedis compare to Lua? Do they do the same thing?

Grzegorz Piwowarek
Guest
Grzegorz Piwowarek

Can you be more specific? Jedis is a Java Redis Client and Lua is a language

JC
Guest
JC

Hi Grzegorz…Sure I can elaborate on my earlier question. As I am learning how to use Redis, Jedis, I am also trying to understand where Lua fits in to the ecosystem. It is my understanding that whatever I can do with Java via Jedis(ie. get data in/out of Redis)…I can do the same thing by calling a Lua script inside Redis. Does that seem true? My confusion comes from…when do I want to do some analysis/work on the data inside a java application or and when should I use Lua? Can you comment on pros/cons?

Grzegorz Piwowarek
Guest
Grzegorz Piwowarek

Well, I did not write that article and I am not really a Redis expert but as I understood it, you can interact with Redis by using Lua or by using a Java client. The advantage of using Jedis would be simply the absence of a learning curve which happens when you are learning a new language

JC
Guest
JC

Do you know if doing most things inside Redis via a Lua script would be faster than pushing all the data out to java and then doing calculations? For our conversation, by faster, I mean orders of magnitude faster…your thoughts are much appreciated.

Kind Regards,
Jeff

Grzegorz Piwowarek
Guest
Grzegorz Piwowarek

Again, I am not really sure and I would be really curious to see the answer of someone more experienced with Redis but since Jedis is a “client”, it should run pretty much with the same speed +/- network overhead.

I suggest going with this one to Stackoverflow and exposing the question to the broader audience. Paste the question link here so that we get some insights too. Thanks!

Bartosz Jaszczak
Guest
Bartosz Jaszczak

I’ve got one question, is there a proper way to test Redis repositories/connections/entities? I’m using Spring Data Redis, and trying to write some tests for it. I’ve found projects like com.github.kstyrc.embedded-redis, but i dont like idea of alpha version of library with last commit 10 months ago in production dependencies. Should i use real/test redis instance? Is there any spring supported way to use embedded one?

Grzegorz Piwowarek
Editor

Yup, doesn’t sound like a good idea. Personally, I’d just dockerize it and manually manage container’s lifecycle when testing