All Products
Search
Document Center

ApsaraDB for Redis:Use pipelining to batch issue commands

Last Updated:Jan 17, 2024

You can use the Redis pipelining mechanism to perform batch operations or improve command execution performance. Pipelining allows you to send multiple commands to the Redis server simultaneously, reducing network latency and improving performance. ApsaraDB for Redis and Tair support the pipelining technique of open source Redis.

Overview of pipelining

Typically, the ping-pong mode is used in the communication between clients and Redis servers. In this mode, the client does not issue a command until the client receives a response to the last command from the server.

Additionally, Redis provides the pipelining mode in which the client batch issues commands without waiting for responses. After the client receives the responses, it matches them to the commands in order and returns the results to the frontend.

The following figure shows how the ping-pong mode and the pipelining mode work.pipeline

Pipelining improves the system efficiency and performance by reducing the network latency associated with round trip time (RTT) and the number of read() and write() system calls.

Pipelining is useful in scenarios where multiple commands need to be quickly submitted to the server and the responses are not required immediately. As such, pipelining can be used as a batch processing tool to optimize the system performance.

Important

In pipelining mode, pipelines exclusively use the connection between the client and the server, and non-pipeline operations cannot be performed until the pipelines are closed. To perform other operations at the same time, you can establish a dedicated connection for pipelines to separate them from other operations.

For more information, see Redis pipelining.

Precautions

  • Pipelining does not ensure atomicity.

    In pipelining mode, the client batch issues commands, whereas the server resolves individual commands and runs them in order. As a result, the server may also run commands from other clients. To ensure atomicity, you can use transaction scripts or Lua scripts.

  • Pipelining does not support rollback in the case of errors.

    If commands to be run are dependent on each other, do not use pipelining.

    Note

    Specific clients, such as redis-py, use transaction commands such as MULTI and EXEC to simulate pipelines. It is important to distinguish between pipelines and transactions and use them appropriately based on your specific requirements. Otherwise, errors may occur. For more information about the limits on transactions, see Transactions.

  • If a large number of commands are run, pipelining may not perform well due to buffer limits of servers and specific clients.

  • Pipelining is fundamentally a client-server interaction pattern that is independent of the server architecture. Cluster instances in proxy mode, cluster instances in direct connection mode, and read/write splitting instances all support pipelining.

    Note

    The cluster architecture has inherent limits. For example, the cluster architecture does not support cross-slot key access in a command. If a client attempts to access a key that does not belong to the current node to which the client is connected, a -MOVED error occurs. When you use pipelining in the cluster architecture, make sure that the commands within pipelines meet the requirements of the cluster architecture. For more information, see Limits on commands supported by cluster instances and read/write splitting instances.

Sample code

Performance comparison

The following code provides the performance comparison between operations with and without pipelines:

package pipeline.kvstore.aliyun.com;
import java.util.Date;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;
public class RedisPipelinePerformanceTest {
        static final String host = "xxxxxx.m.cnhza.kvstore.aliyuncs.com";
        static final int port = 6379;
        static final String password = "password";
        public static void main(String[] args) {
            Jedis jedis = new Jedis(host, port);
                //The password of the ApsaraDB for Redis instance.
                String authString = jedis.auth(password);// password
                if (!authString.equals("OK")) {
                    System.err.println("AUTH Failed: " + authString);
                    jedis.close();
                    return;
                }
                //Run several commands consecutively.
                final int COUNT=5000;
                String key = "KVStore-Tanghan";
                //1 ---Without pipelines---
                jedis.del(key);//Initialize the key.
                Date ts1 = new Date();
                for (int i = 0; i < COUNT; i++) {
                    //Send a request and receive a response.
                    jedis.incr(key);
                }
                Date ts2 = new Date();
                System.out.println("Without pipelines > value is: "+jedis.get(key)+" > Time elapsed: " + (ts2.getTime() - ts1.getTime())+ "ms");
                //2 ----With pipelines---
                jedis.del(key);//Initialize the key.
                Pipeline p1 = jedis.pipelined();
                Date ts3 = new Date();
                for (int i = 0; i < COUNT; i++) {
                    //Send the request. 
                    p1.incr(key);
                }
                //Receive the response.
                p1.sync();
                Date ts4 = new Date();
                System.out.println("With pipelines > value is:"+jedis.get(key)+" > Time elapsed:" + (ts4.getTime() - ts3.getTime())+ "ms");
                jedis.close();
        }
    }

After you access the ApsaraDB for Redis instance with the correct endpoint and password and run the preceding Java code, the following output is displayed. The output shows that the performance is enhanced with pipelines.

Without pipelines > value: 5,000 > Time elapsed: 5,844 ms
With pipelines > value: 5000 > Time elapsed: 78 ms

Methods to handle responses

When you use pipelining in Jedis, you can use two methods to handle responses. For more information, see the following sample code:

package pipeline.kvstore.aliyun.com;
import java.util.List;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;
import redis.clients.jedis.Response;
    public class PipelineClientTest {
        static final String host = "xxxxxxxx.m.cnhza.kvstore.aliyuncs.com";
        static final int port = 6379;
        static final String password = "password";
        public static void main(String[] args) {
            Jedis jedis = new Jedis(host, port);
                // The password of the ApsaraDB for Redis instance.
                String authString = jedis.auth(password);// password
                if (!authString.equals("OK")) {
                    System.err.println("AUTH Failed: " + authString);
                    jedis.close();
                    return;
                }
                String key = "KVStore-Test1";
                jedis.del(key);// Initialize the key.
                //-------- Method 1
                Pipeline p1 = jedis.pipelined();
                System.out.println("-----Method 1-----");
                for (int i = 0; i < 5; i++) {
                    p1.incr(key);
                    System.out.println("Pipeline sends requests");
                }
                // After the pipeline sends all requests, the client starts to receive responses.
                System.out.println("Sending requests completed. Start to receive responses");
                List<Object> responses = p1.syncAndReturnAll();
                if (responses == null || responses.isEmpty()) {
                    jedis.close();
                    throw new RuntimeException("Pipeline error: no responses received");
                }
                for (Object resp : responses) {
                    System.out.println("Pipeline receives response: " + resp.toString());
                }
                System.out.println();
                //-------- Method 2
                System.out.println("-----Method 2-----");
                jedis.del(key);// Initialize the key.
                Pipeline p2 = jedis.pipelined();  
                // Declare the responses first.
                Response<Long> r1 = p2.incr(key); 
                System.out.println("Pipeline sends requests");
                Response<Long> r2 = p2.incr(key);
                System.out.println("Pipeline sends requests");
                Response<Long> r3 = p2.incr(key);
                System.out.println("Pipeline sends requests");
                Response<Long> r4 = p2.incr(key);  
                System.out.println("Pipeline sends requests");
                Response<Long> r5 = p2.incr(key);
                System.out.println("Pipeline sends requests");
                try{  
                    r1.get(); // Errors occur because the client has not started to receive responses.
                }catch(Exception e){  
                    System.out.println(" <<< Pipeline error: The client has not started to receive responses. >>> ");  
                }  
             // After the pipeline sends all requests, the client starts to receive responses.
                System.out.println("Sending requests completed. Start to receive responses");
                p2.sync();  
                System.out.println("Pipeline receives response: " + r1.get());  
                System. out. println ("Pipeline receives response: " + r2.get ());  
                System. out. println ("Pipeline receives response: " + r3.get ());
                System. out. println ("Pipeline receives response: " + r4.get ());
                System. out. println ("Pipeline receives response: " + r5.get ());
                jedis.close();
            }
    }

After you access the ApsaraDB for Redis instance with the correct endpoint and password and run the Java code, the following output is displayed:

----- Method 1 -----
Pipeline sends requests
Pipeline sends requests
Pipeline sends requests
Pipeline sends requests
Pipeline sends requests
After the pipeline sends all requests, the client starts to receive responses.
Pipeline receives response: 1
Pipeline receives response: 2
Pipeline receives response: 3
Pipeline receives response: 4
Pipeline receives response: 5
----- Method 2 -----
Pipeline sends requests
Pipeline sends requests
Pipeline sends requests
Pipeline sends requests
Pipeline sends requests
 <Pipeline error: The client has not started to receive responses> 
After the pipeline sends all requests, the client starts to receive responses.
Pipeline receives response: 1
Pipeline receives response: 2
Pipeline receives response: 3
Pipeline receives response: 4
Pipeline receives response: 5