Using Jedis and Redis Persistence Mechanisms Effectively

Connecting to Redis with Jedis

Jedis is a Java client library for connecting to and interacting with a Redis server. Alternatives include Spring Data Redis and Lettuce.

Maven Dependency

Add the following dependency to your pom.xml:

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

Basic Connection and Operations

import redis.clients.jedis.Jedis;

public class RedisExample {
    public static void main(String[] args) {
        // Connect to Redis
        Jedis jedis = new Jedis("localhost", 6379);

        // Set and get a string value
        jedis.set("greeting", "Hello, Redis!");
        String value = jedis.get("greeting");
        System.out.println(value);  // Output: Hello, Redis!

        // Close the connection
        jedis.close();
    }
}

Working with Data Structures

Lists

import redis.clients.jedis.Jedis;
import java.util.List;

public class ListExample {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("localhost", 6379);

        jedis.lpush("mylist", "item3", "item2", "item1");
        jedis.rpush("mylist", "item4");
        
        List<String> items = jedis.lrange("mylist", 0, -1);
        for (String item : items) {
            System.out.print(item + " ");
        }
        System.out.println();
        
        System.out.println("List length: " + jedis.llen("mylist"));
        
        jedis.close();
    }
}

Hashes

import redis.clients.jedis.Jedis;
import java.util.Map;

public class HashExample {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("localhost", 6379);

        jedis.hset("user:1000", "name", "Alice");
        jedis.hset("user:1000", "age", "30");
        
        Map<String, String> user = jedis.hgetAll("user:1000");
        System.out.println(user);  // Output: {name=Alice, age=30}
        
        System.out.println("Hash size: " + jedis.hlen("user:1000"));
        
        jedis.close();
    }
}

Implementing Rate Limiting with Jedis

A common use case is limiting the number of operations a user can perform within a time window. This example shows how to restrict User A to 3 calls per 10 seconds and User B to 6 calls per 10 seconds.

import redis.clients.jedis.Jedis;

public class RateLimiter {
    private String userId;
    private int maxRequests;
    private int windowSeconds;

    public RateLimiter(String userId, int maxRequests, int windowSeconds) {
        this.userId = userId;
        this.maxRequests = maxRequests;
        this.windowSeconds = windowSeconds;
    }

    public boolean allowRequest() {
        try (Jedis jedis = new Jedis("localhost", 6379)) {
            String key = "ratelimit:" + userId;
            Long current = jedis.incr(key);
            
            if (current == 1) {
                jedis.expire(key, windowSeconds);
            }
            
            if (current <= maxRequests) {
                System.out.println("User " + userId + " request " + current + "/" + maxRequests + " allowed.");
                return true;
            } else {
                System.out.println("User " + userId + " rate limit exceeded. Request blocked.");
                return false;
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        RateLimiter userA = new RateLimiter("A", 3, 10);
        RateLimiter userB = new RateLimiter("B", 6, 10);

        for (int i = 0; i < 10; i++) {
            userA.allowRequest();
            userB.allowRequest();
            Thread.sleep(1000);
        }
    }
}

Using a Jedis Connection Pool

A connection pool improves performance by reusing connections instead of creating new ones for every operation. This singleton-based utility reads configuration from a properties file.

jedis.properties

jedis.host=localhost
jedis.port=6379
jedis.maxTotal=30
jedis.maxIdle=10

RedisPool.java

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import java.util.ResourceBundle;

public class RedisPool {
    private static final JedisPool pool;

    static {
        ResourceBundle bundle = ResourceBundle.getBundle("jedis");
        String host = bundle.getString("jedis.host");
        int port = Integer.parseInt(bundle.getString("jedis.port"));
        int maxTotal = Integer.parseInt(bundle.getString("jedis.maxTotal"));
        int maxIdle = Integer.parseInt(bundle.getString("jedis.maxIdle"));

        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(maxTotal);
        config.setMaxIdle(maxIdle);

        pool = new JedisPool(config, host, port);
    }

    public static Jedis getConnection() {
        return pool.getResource();
    }
}

Redis Persistence

Persistence in Redis refers to saving the in-memory data to permanent storage (disk) so that it can be recovered after a restart.

Persistence Methods

  1. RDB (Redis Database): Creates point-in-time snapshots of the dataset at specified intervals. Stores the data state.
  2. AOF (Append Only File): Logs every write operation received by the server. These logs are replayed at startup to reconstruct the dataset. Stores the operation history.
  3. No Persistence: Data exists only while the server is running.
  4. RDB + AOF: Both methods can be enabled simultaneously. On restart, Redis uses the AOF file to reconstruct data because it is typically more complete.

RDB (Snapshot) Persistence

RDB creates binary snapshot files (.rdb).

  • save command: Synchronously blocks the server until the snapshot is complete. Not recommended for production.
  • bgsave command: Asynchronously forks a child process to create the snapshot. The parent process continues serving requests.
  • Configuration: Automatic snapshots can be configured.

Configurtaion Example (redis.conf)

# RDB file name
dbfilename dump.rdb

# Directory to store RDB files
dir /var/lib/redis

# Compress RDB files (yes/no)
rdbcompression yes

# Perform checksum on RDB files
rdbchecksum yes

# Snapshot triggers: <seconds> <changes>
save 900 1        # At least 1 change in 900 seconds (15 min)
save 300 10       # At least 10 changes in 300 seconds (5 min)
save 60 10000     # At least 10000 changes in 60 seconds (1 min)

# Stop writing if bgsave fails
stop-writes-on-bgsave-error yes

Comparison of RDB Triggers

Method Blocking Child Process Memory Overhead
save Yes No No
bgsave No Yes Yes
Configuration save No Yes Yes

Pros and Cons of RDB

Pros

  • Compact binary file, efficient storage.
  • Excellent for backups and disaster recovery due to point-in-time snapshots.
  • Very fast recovery from RDB compared to AOF.

Cons

  • Not real-time; possible data loss between snapshots.
  • bgsave uses fork(), which can be resource-intensive.
  • RDB format may not be compatible across different Redis versions.

AOF (Append Only File) Persistence

AOF logs every write command in a human-protocol-readable format.

Write Strategies

Strategy Description Data Safety Performance
always Every write is flushed to disk. Zero data loss on crash Lowest
everysec Buffered writes are flushed every second. May lose up to 1 second of data on crash Good
no Operating system decides when to flush. Unpredictable. Less predictable High

Default is everysec.

Configuration Example (redis.conf)

# Enable AOF
appendonly yes

# AOF file name
appendfilename "appendonly.aof"

# Write strategy: always, everysec, no
appendfsync everysec

# No fsync during AOF rewrite (improves performance)
no-appendfsync-on-rewrite no

AOF Rewrite

Over time, the AOF file can grow large and contain redundant commands (e.g., multiple updates to the same key). AOF rewrite compacts the file by generating the minimal set of commands needed to reconstruct the current dataset.

Configuration

# Trigger rewrite when AOF file size exceeds 100% of last rewrite size
auto-aof-rewrite-percentage 100

# Minimum size to trigger rewrite (avoids frequent rewrites on small files)
auto-aof-rewrite-min-size 64mb

Manual Trigger

BGREWRITEAOF

AOF Rewrite Rules

  • Expired keys are excluded.
  • Multiple commands affecting the same key are merged into a single command.
  • For lists, sets, hashes, etc., at most 64 elements are written per command to prevent client buffer overflow.

Rewrite Mechanism

Redis forks a child process that receives all incoming write commands in a special `` buffer. Once the child finishes creating the new AOF, the parent signals it to replace the old AOF with the new one.

RDB vs AOF: Summary

Aspect RDB AOF
Data Type Snapshot (data state) Log (operations)
File Size Smaller (compressed binary) Larger (text protocol)
Recovery Speed Very fast Slower (replays all operations)
Real-time Safety Lower (point-in-time) Higher (up to 1 second with everysec)
Resource Usage Lower for writing, fork() Higher for writing, less for fork()

When to Use

  • AOF: If data loss of more than a few seconds is unacceptable. AOF is often the primary choice for data safety.
  • RDB: If fast recovery is more important than minimal data loss, or for backups and disaster recovery scenarios.
  • Combined: Enable both. Redis uses AOF on restart for reconstruction (since it is more complete) while RDB serves as a backup mechanism.

Practical Persistence Decision Matrix

Use Case Persist? Reason
Database primary key generation No Persisting a counter won't prevent duplicates after a crash; you still need to scan the DB.
Hot data cache (speed up DB reads) No Data already exists in the underlying database.
Shopping cart No Data is typically temporary or stored in the database.
Flash sales, limited coupon codes, activation keys Yes These are high-speed, ephemeral data that the master DB may not track in real-time.
Ordered task queues Yes Need to preserve order and not lose tasks.
Latest news feed Yes To avoid fetching all new items from the DB after restart.
Complex graph relations (e.g., friend suggestions) No Better to recompute from the primary DB.
Blacklists/whitelists Yes Long-term rules are typically stored, short-term ones might be persisted if important.
Leaderboards & rankings Yes Persisting ensures no loss of scores between restarts.
Instant messaging / task queues No Better handled by dedicated message brokers (e.g., RabbitMQ, Kafka).
Per-call billing or service control Yes Depends on criticality; may not require persistence for less vital services.

Tags: Jedis Redis Persistence RDB AOF

Posted on Wed, 30 Sep 2026 16:45:44 +0000 by gapern