SoFunction
Updated on 2025-04-14

Detailed explanation of three methods of Redis to implement delayed tasks

1. Preface

Delayed Task refers to performing corresponding tasks at a certain point in the future. That is, a delay task is a planned task that is scheduled to be executed after a specific time, rather than immediately.

There are several common usage scenarios for delayed tasks:

1. Send notifications or messages regularly:

Send timed text messages, emails or in-app messages, such as registration confirmation, order status updates, promotional notifications, etc.

Press news, weather forecasts, stock prices and other real-time information regularly.

2. Asynchronous processing and background tasks:

Schedule time-consuming operations as delayed tasks to avoid blocking the main thread or user interface and improve system response performance.

Perform batch data processing, such as log analysis, data report generation, etc.

3. Cache management and expiration processing:

Timely clean out expired cache data and free up storage space.

Update the data in the cache to maintain the timeliness and accuracy of the data.

4. Plan tasks and scheduled scheduling:

Perform system maintenance tasks at specific times, such as database backup, system update, etc.

Start or shut down services regularly to save resources or meet business needs.

5. Order and payment processing:

If the user does not pay within a period of time after the user places an order, the order will be automatically cancelled.

Check the payment status of the order regularly and update the corresponding order information.

6. Retry and failed recovery mechanism:

When an operation fails, it can be automatically retryed after a delay of a period of time to improve success rate.

Implement timeout release of distributed locks to avoid deadlocks.

7. Reminders and schedule management:

Set agenda reminder, such as meetings, birthdays, anniversary, etc.

Remind users regularly to complete tasks or perform certain activities.

8. Timed data collection and reporting:

Collect data regularly from sensors, devices, or external systems.

Report the application usage, statistics or user behavior analysis regularly.

How to implement delayed tasks

Redis itself does not directly provide the function of delayed tasks, but delayed tasks can be manually implemented in Redis through some strategies and means.

The main methods to use Redis to implement delay tasks are as follows:

1. Use the event notification of the expiration key to execute the delay task: Turn on the expiration key notification, triggering the time when the key value in Redis expires, and implementing the delay code in the event. However, because the key of Redis will not be deleted in time when it expires, this expired event is not guaranteed to be triggered immediately, so this method is rarely used to implement delay tasks (because it is extremely unstable).

2. Use ZSet to execute delayed tasks: Insert the delayed execution time and task ID in the ZSet, as shown in the following command:

ZADD delay_tasks <timestamp> <task_id>

Then, start a background thread or timing task, and regularly obtain tasks that have reached the execution time from the ordered set through the ZRANGEBYSCORE command, that is, tasks whose score is less than or equal to the current time, and execute them to achieve delayed tasks.

3. Use Redisson to perform delay tasks: In the Redisson framework, an RDelayedQueue is provided for implementing delay queues, which is simple and convenient to use and is recommended.

3. Code implementation

3.1. Expiration key notification event implementation

Redis provides keyspace notification function, and can send notifications when a key changes (expires). You can implement delayed tasks in combination with EXPIRE expire commands and keyspace notifications.

When setting an expiration time for a key, Redis sends a notification once the key expires. You can subscribe to this notification and perform tasks when you receive the notification. But this approach may not be accurate enough and depends on Redis's internal mechanism.

Its implementation steps are:

Setting the Redis expiration key notification event to enable the Redis expiration key notification event, you can dynamically enable the keyspace notification function by executing the "CONFIG SET notify-keyspace-events KEA" command without restarting the Redis server.

To set the expiration key, you can set a key to expire after 3 seconds (execute after 3s) through the command "SET mykey "myvalue" EX 3".

Write a listener to subscribe to Redis's keyspace notifications. This can be achieved by using Redis's publish/subscribe function. The specific implementation code is as follows (taking the use of the Jedis framework as an example):

import ;  
import ;  
  
public class RedisKeyspaceNotifier {  
  
    public static void main(String[] args) {  
        // Create Jedis instance        Jedis jedis = new Jedis("localhost", 6379);  
  
        //Configure keyspace notifications (usually this step is done in the Redis configuration file, but it can also be configured at runtime)        ("notify-keyspace-events", "KEA");  
  
        // Subscribe to keyspace notifications        (new KeyspaceNotificationSubscriber(), "__keyevent@0__:expired");  
    }  
  
    static class KeyspaceNotificationSubscriber extends JedisPubSub {  
  
        @Override  
        public void onMessage(String channel, String message) {  
            ("Received message from channel: " + channel + ", message: " + message);  
            // Process the received keyspace notification here            // For example, if message is a task ID that needs to be processed, you can trigger the corresponding task processing logic here        }  
  
        @Override  
        public void onSubscribe(String channel, int subscribedChannels) {  
            ("Subscribed to channel: " + channel);  
        }  
  
        @Override  
        public void onUnsubscribe(String channel, int subscribedChannels) {  
            ("Unsubscribed from channel: " + channel);  
        }  
    }  
}

However, because Redis's key will not be deleted in time when expired, Redis uses lazy deletion and regular deletion, so this expiration event is not guaranteed to be triggered immediately, so this method is rarely used to implement delayed tasks (because it is extremely unstable).

3.2. Use ZSet to implement delayed tasks

You can store the task and its execution time as members and scores in ZSET, and then use a background task (such as a timed task or daemon) to periodically check the ZSET, find members whose score (i.e., execution time) is less than or equal to the current time, and perform the corresponding task. After execution, delete the member from ZSET, and the specific implementation code is as follows:

import ;  
  
import ;  
  
public class RedisDelayedTaskDemo {  
  
    private static final String ZSET_KEY = "delayed_tasks";  
    private static final String REDIS_HOST = "localhost";  
    private static final int REDIS_PORT = 6379;  
  
    public static void main(String[] args) {  
        Jedis jedis = new Jedis(REDIS_HOST, REDIS_PORT);  
  
        // Add delay task        addDelayedTask(jedis, "task1", () / 1000 + 5); // Execute after 5 seconds        addDelayedTask(jedis, "task2", () / 1000 + 10); // Execute after 10 seconds  
        // Simulation timed task checker        new Thread(() -&gt; {  
            while (true) {  
                try {  
                    // Check and execute expired tasks                    checkAndExecuteTasks(jedis);  
                    (1000); // Check once every second                } catch (InterruptedException e) {  
                    ();  
                }  
            }  
        }).start();  
    }  
  
    private static void addDelayedTask(Jedis jedis, String task, long executeTime) {  
        (ZSET_KEY, executeTime, task);  
        ("Added task: " + task + " with execution time: " + executeTime);  
    }  
  
    private static void checkAndExecuteTasks(Jedis jedis) {  
        long currentTime = () / 1000;  
        Set&lt;String&gt; tasks = (ZSET_KEY, 0, currentTime);  
  
        for (String task : tasks) {  
            (ZSET_KEY, task); // Remove task from ordered collection            executeTask(task); // Execute tasks        }  
    }  
  
    private static void executeTask(String task) {  
        ("Executing task: " + task);  
        // Add actual task execution logic here    }  
}

In this example, we first use the addDelayedTask method to add tasks to the ordered collection of Redis and set their execution time. We then start a thread to simulate the timed task checker, which checks once a second for any task to expire and performs the expired task.

3.3 Using Redisson's Delay Queue (Commonly Used)

In the Redisson framework, an RDelayedQueue is provided for implementing delay queues. It is simple and convenient to use. Its specific implementation is as follows:

import ;
import ;
import ;
import ;
 
import ;
 
public class RDelayedQueueDemo {
 
    public static void main(String[] args) throws InterruptedException {
        // Create Redisson client        Config config = new Config();
        ().setAddress("redis://127.0.0.1:6379");
        RedissonClient redisson = (config);
 
        // Get the delay queue        RDelayedQueue&lt;String&gt; delayedQueue = 
            ("delayedQueue");
 
        // Add delay task        ("task1", 5, );
 
        // Listen and handle delayed tasks        Thread listenerThread = new Thread(() -&gt; {
            while (true) {
                try {
                    // Wait and get the expired task through the take method                    String task = ();
                    ("Handle task: " + task);
                } catch (InterruptedException e) {
                    break;
                }
            }
        });
        ();
    }
}

In the above example, we first create a Redisson client that specifies the use of a single-node Redis server through the configuration file. Then we get a delay queue RDelayedQueue and add a delay task with a delay time of 5 seconds. Then we listen through threads and process tasks in the delay queue.

Analysis of advantages and disadvantages of realizing delayed tasks

advantage:

  • Lightweight and high performance: Redis is a data structure storage system in memory, so it reads and writes very quickly. Store task information in Redis to quickly access operations.
  • Simple and easy to use: Redis's API is simple and clear, and easy to integrate into existing application systems.

shortcoming:

  • Limited accuracy: Redis's delay tasks rely on the system's timing check mechanism rather than accurate timers. This means that there may be some delay in the execution of tasks, especially when the system is loaded high or the inspection interval is long.
  • Limited functionality: Redis provides delayed task functionality that may be relatively simple compared to professional task scheduling systems. For complex task scheduling requirements, such as task dependencies, task priority, etc., additional logic may be required to implement them.
  • Poor stability: There is no retry mechanism and ACK confirmation mechanism for delay tasks using Redis, so the stability is relatively poor.
  • Single point of failure risk: If the Redis cluster or master-slave replication is not configured correctly, failure of a single Redis instance can cause the entire delayed task system to fail.

5. Summary

Above we have summarized several solutions to implement delay tasks using redis. In some simple scenarios, you can directly use the delay queue provided by redisson to implement delay tasks, which is very easy to get started. In complex and large-scale scenarios, it is recommended to use professional task scheduling systems, such as xxl-job, Quartz, etc.

This is the end of this article about the three methods of Redis to implement delayed tasks. For more related Redis delayed tasks, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!