Skip to content

Instantly share code, notes, and snippets.

@warrenzhu25
Last active August 18, 2022 00:36
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save warrenzhu25/1beb02a09b6afd41dff2c27c53918ce7 to your computer and use it in GitHub Desktop.
Save warrenzhu25/1beb02a09b6afd41dff2c27c53918ce7 to your computer and use it in GitHub Desktop.

Lettuce vs Jedis

  • Jedis - A blazingly small and sane redis java client

  • Lettuce - Advanced Redis client for thread-safe sync, async, and reactive usage. Supports Cluster, Sentinel, Pipelining, and codecs.

  • If you haven't made your decision, I would recommend Lettuce

  • If you alread used Jedis in your project, stick it unless you need Cluster SSL support.

Why Lettuce

  1. Used Netty - asynchronous event-driven high performance network framework to do network communication, and it supports sync, async and react pattern.
  2. Quicker community response. I opened issues in both community, now Lettuce already fixed it and Jedis is still in process
  3. Cleaner design and code. I really doesn't like the constructor of Jeids, JedisPool and JedisCluster
  4. Better docmentation. You could find everything you need in the offical doc

Connection Pool

When to use connection pool

  • Connection pool is only needed when you need many dedicated connections such as transactions or blocking operation with each worker thread get its dedicated connection
  • Use many connections doesn't necessarily improve the performance and concurrency since Redis is single threaded and every reqeust is handled serially.
  • The Connection object is thread-safe and can be used from multiple threads at the same time.
  • This pool should be configured once and reused.
  • Make sure to return the Connection back to the pool when done, otherwise you will leak it. Recommend to use try-with-resource, which return it automatically.

Choose pool config with care

Setting Description
maxTotal This setting controls the max number of connections that can be created at a given time. Note that each connection does have some memory and CPU overhead, so setting this to a very high value may have negative side effects. If not set, the default value is 8, which is probably too low for most applications. When chosing a value, consider how many concurrent calls into Redis you think you will have under load.
maxIdle This is the max number of connections that can be idle in the pool without being immediately evicted (closed). If not set, the default value is 8. I would recommend that this setting be configured the same as maxTotal to help avoid connection ramp-up costs when your application has many bursts of load in a short period of time. If a connection is idle for a long time, it will still be evicted until the idle connection count hits minIdle (described below).
minIdle This is the number of "warm" connections (e.g. ready for immediate use) that remain in the pool even when load has reduced. If not set, the default is 0. When choosing a value, consider your steady-state concurrent requests to Redis. For instance, if your application is calling into Redis from 10 threads simultaneously, then you should set this to at least 10 (probably a bit higher to give you some room.
blockWhenExhausted This controls behavior when a thread asks for a connection, but there aren't any that are free and the pool can't create more (due to maxTotal). If set to true, the calling thread will block for maxWaitMillis before throwing an exception. The default is true and I recommend true for production environments. You could set it to false in testing environments to help you more easily discover what value to use for maxTotal.
maxWaitMillis How long to wait in milliseconds if calling Pool.getResource() will block. The default is -1, which means block indefinitely. I would set this to the same as the socketTimeout configured. Related to blockWhenExhausted.
TestOnBorrow Controls whether or not the connection is tested before it is returned from the pool. The default is false. Setting to true may increase resilience to connection blips but may also have a performance cost when taking connections from the pool. In my quick testing, I saw a noticable increase in the 50th percentile latencies, but no significant increase in 98th percentile latencies.

Log pool usage periodically

  • Debugging performance problems due to JedisPool contention issues will be easier if you log the pool usage regularly.
  • If you ever get an error when trying to get a connection from the pool, you should definitely log usage stats.
  • Sample code here

Important client config

Setting Description
connectTimeout How long to allow for new connections to be established (in milliseconds). In general, this should be at least 5000ms. If your client application tends to have high spikes CPU usage, setting this to 15000ms or 20000ms would be a good idea.
port In Azure, 6379 is non-ssl and 6380 is SSL/TLS. Important Note: 6379 is disabled by default - you have to explicitly enable this insecure port if you wish to use it.

Lettuce

Async vs Sync vs React

Pipeline used implicitly

Master/Slave

Cluster

Important Lettuce cluster config to consider

Setting Description
enablePeriodicRefresh Periodically refresh nodes topology
enableAllAdaptiveRefreshTriggers Recommend to enable this. This will trigger nodes topology refresh when encounter MOVED, ASKED or PESISTENT_RECONNECT (which means refreshTriggersReconnectAttempts for a specific node has been reached). This means if cluster topology changed, JedisCluster will take as long as reconnectAttempts * connectTimeout in the worst case before refresh local topology. This could happen during patching or failover

Sample code

Jedis

Jedis instances are not thread-safe

  • Don't use the same Jedis instance from multiple threads at the same time.
  • Doing this will result in socket connection errors/resets or strange error messages like "expected '$' but got ' '".

Use Pipelining

  • This will improve the throughput of the application. Read more about redis pipelining here https://redis.io/topics/pipelining.
  • Jedis does not do pipelining automatically for you. You have to call diffeent APIs in order to get the significant performance benefits that can come from using pipelining.
  • Examples can be found here

JedisCluster

  • JedisCluster maintains a pool for each node slot, so no need to do pool again. So pool config take effect for all the pools.
  • JedisCluster doesn't support SSL. You need build your own version based on this PR
  • Sample code here

Important Jedis cluster config to consider

Setting Description
maxAttempts Max times of reconnect attempts before refresh local in-memory cluster slots map. This means if cluster topology changed, JedisCluster will take as long as (maxAttempts + 1) * connectTimeout in the worst case before refresh local topology. This could happen during patching or failover

Sample code

@mrniko
Copy link

mrniko commented Jun 21, 2019

This article missing yet another Redis Java client - Redisson

@ijmac3
Copy link

ijmac3 commented Jul 1, 2019

This article missing yet another Redis Java client - Redisson

+1 to this. We are in the early stages of moving our product to Microsoft Azure and are curious why Redisson does not appear on this list. From our investigations it seems to be the best of the currently available Java clients.

@warrenzhu25
Copy link
Author

Sorry I'm unfamiliar with Redisson. Feel free to add your part.

@akshitjain
Copy link

This article missing yet another Redis Java client - Redisson

Yeah Redisson should also be included as it is being actively developed and is a recommended Java client by Redis Labs.
This article is being linked to Azure Recommendations so it should be updated as Redisson is now being accepted widely

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment