Learn how to use Redis pipelines and transactions
Redis lets you send a sequence of commands to the server together in a batch. There are two types of batch that you can use:
To execute commands in a pipeline, you first create a pipeline object
and then add commands to it using methods that resemble the standard
command methods (for example, set() and get()). The commands are
buffered in the pipeline and only execute when you call the sync()
method on the pipeline object.
The main difference with the pipeline commands is that they return
Response<Type> objects, where Type is the return type of the
standard command method. A Response object contains a valid result
only after the pipeline has finished executing. You can access the
result using the Response object's get() method.
importjava.util.List;importredis.clients.jedis.UnifiedJedis;importredis.clients.jedis.AbstractPipeline;importredis.clients.jedis.AbstractTransaction;importredis.clients.jedis.Response;import staticorg.junit.jupiter.api.Assertions.assertEquals;publicclass PipeTransExample{publicvoidrun(){UnifiedJedisjedis=newUnifiedJedis("redis://localhost:6379");// Make sure you close the pipeline after use to release resources// and return the connection to the pool.try(AbstractPipelinepipe=jedis.pipelined()){for(inti=0;i<5;i++){pipe.set(String.format("seat:%d",i),String.format("#%d",i));}pipe.sync();}try(AbstractPipelinepipe=jedis.pipelined()){Response<String>resp0=pipe.get("seat:0");Response<String>resp3=pipe.get("seat:3");Response<String>resp4=pipe.get("seat:4");pipe.sync();// Responses are available after the pipeline has executed.System.out.println(resp0.get());// >>> #0System.out.println(resp3.get());// >>> #3System.out.println(resp4.get());// >>> #4}try(AbstractTransactiontrans=jedis.multi()){trans.incrBy("counter:1",1);trans.incrBy("counter:2",2);trans.incrBy("counter:3",3);trans.exec();}System.out.println(jedis.get("counter:1"));// >>> 1System.out.println(jedis.get("counter:2"));// >>> 2System.out.println(jedis.get("counter:3"));// >>> 3// Set initial value of `shellpath`.jedis.set("shellpath","/usr/syscmds/");// Start the transaction and watch the key we are about to update.try(AbstractTransactiontrans=jedis.transaction(false)){// create a Transaction object without sending MULTI commandtrans.watch("shellpath");// send WATCH command(s)trans.multi();// send MULTI commandStringcurrentPath=jedis.get("shellpath");StringnewPath=currentPath+":/usr/mycmds/";// Commands added to the `trans` object// will be buffered until `trans.exec()` is called.Response<String>setResult=trans.set("shellpath",newPath);List<Object>transResults=trans.exec();// The `exec()` call returns null if the transaction failed.if(transResults!=null){// Responses are available if the transaction succeeded.System.out.println(setResult.get());// >>> OK// You can also get the results from the list returned by// `trans.exec()`.for(Objectitem:transResults){System.out.println(item);}// >>> OKSystem.out.println(jedis.get("shellpath"));// >>> /usr/syscmds/:/usr/mycmds/}}jedis.close();}}A transaction works in a similar way to a pipeline. Create a
transaction object with the multi() command, call command methods
on that object, and then call the transaction object's
exec() method to execute it. You can access the results
from commands in the transaction using Response objects, as
you would with a pipeline. However, the exec() method also
returns a List<Object> value that contains all the result
values in the order the commands were executed (see
Watch keys for changes below for
an example that uses the results list).
importjava.util.List;importredis.clients.jedis.UnifiedJedis;importredis.clients.jedis.AbstractPipeline;importredis.clients.jedis.AbstractTransaction;importredis.clients.jedis.Response;import staticorg.junit.jupiter.api.Assertions.assertEquals;publicclass PipeTransExample{publicvoidrun(){UnifiedJedisjedis=newUnifiedJedis("redis://localhost:6379");// Make sure you close the pipeline after use to release resources// and return the connection to the pool.try(AbstractPipelinepipe=jedis.pipelined()){for(inti=0;i<5;i++){pipe.set(String.format("seat:%d",i),String.format("#%d",i));}pipe.sync();}try(AbstractPipelinepipe=jedis.pipelined()){Response<String>resp0=pipe.get("seat:0");Response<String>resp3=pipe.get("seat:3");Response<String>resp4=pipe.get("seat:4");pipe.sync();// Responses are available after the pipeline has executed.System.out.println(resp0.get());// >>> #0System.out.println(resp3.get());// >>> #3System.out.println(resp4.get());// >>> #4}try(AbstractTransactiontrans=jedis.multi()){trans.incrBy("counter:1",1);trans.incrBy("counter:2",2);trans.incrBy("counter:3",3);trans.exec();}System.out.println(jedis.get("counter:1"));// >>> 1System.out.println(jedis.get("counter:2"));// >>> 2System.out.println(jedis.get("counter:3"));// >>> 3// Set initial value of `shellpath`.jedis.set("shellpath","/usr/syscmds/");// Start the transaction and watch the key we are about to update.try(AbstractTransactiontrans=jedis.transaction(false)){// create a Transaction object without sending MULTI commandtrans.watch("shellpath");// send WATCH command(s)trans.multi();// send MULTI commandStringcurrentPath=jedis.get("shellpath");StringnewPath=currentPath+":/usr/mycmds/";// Commands added to the `trans` object// will be buffered until `trans.exec()` is called.Response<String>setResult=trans.set("shellpath",newPath);List<Object>transResults=trans.exec();// The `exec()` call returns null if the transaction failed.if(transResults!=null){// Responses are available if the transaction succeeded.System.out.println(setResult.get());// >>> OK// You can also get the results from the list returned by// `trans.exec()`.for(Objectitem:transResults){System.out.println(item);}// >>> OKSystem.out.println(jedis.get("shellpath"));// >>> /usr/syscmds/:/usr/mycmds/}}jedis.close();}}Redis supports optimistic locking to avoid inconsistent updates to different keys. The basic idea is to watch for changes to any keys that you use in a transaction while you are are processing the updates. If the watched keys do change, you must restart the updates with the latest data from the keys. See Transactions for more information about optimistic locking.
The code below reads a string
that represents a PATH variable for a command shell, then appends a new
command path to the string before attempting to write it back. If the watched
key is modified by another client before writing, the transaction aborts.
Note that you should call read-only commands for the watched keys synchronously on
the usual client object (called jedis in our examples) but you still call commands
for the transaction on the transaction object.
For production usage, you would generally call code like the following in a loop to retry it until it succeeds or else report or log the failure.
importjava.util.List;importredis.clients.jedis.UnifiedJedis;importredis.clients.jedis.AbstractPipeline;importredis.clients.jedis.AbstractTransaction;importredis.clients.jedis.Response;import staticorg.junit.jupiter.api.Assertions.assertEquals;publicclass PipeTransExample{publicvoidrun(){UnifiedJedisjedis=newUnifiedJedis("redis://localhost:6379");// Make sure you close the pipeline after use to release resources// and return the connection to the pool.try(AbstractPipelinepipe=jedis.pipelined()){for(inti=0;i<5;i++){pipe.set(String.format("seat:%d",i),String.format("#%d",i));}pipe.sync();}try(AbstractPipelinepipe=jedis.pipelined()){Response<String>resp0=pipe.get("seat:0");Response<String>resp3=pipe.get("seat:3");Response<String>resp4=pipe.get("seat:4");pipe.sync();// Responses are available after the pipeline has executed.System.out.println(resp0.get());// >>> #0System.out.println(resp3.get());// >>> #3System.out.println(resp4.get());// >>> #4}try(AbstractTransactiontrans=jedis.multi()){trans.incrBy("counter:1",1);trans.incrBy("counter:2",2);trans.incrBy("counter:3",3);trans.exec();}System.out.println(jedis.get("counter:1"));// >>> 1System.out.println(jedis.get("counter:2"));// >>> 2System.out.println(jedis.get("counter:3"));// >>> 3// Set initial value of `shellpath`.jedis.set("shellpath","/usr/syscmds/");// Start the transaction and watch the key we are about to update.try(AbstractTransactiontrans=jedis.transaction(false)){// create a Transaction object without sending MULTI commandtrans.watch("shellpath");// send WATCH command(s)trans.multi();// send MULTI commandStringcurrentPath=jedis.get("shellpath");StringnewPath=currentPath+":/usr/mycmds/";// Commands added to the `trans` object// will be buffered until `trans.exec()` is called.Response<String>setResult=trans.set("shellpath",newPath);List<Object>transResults=trans.exec();// The `exec()` call returns null if the transaction failed.if(transResults!=null){// Responses are available if the transaction succeeded.System.out.println(setResult.get());// >>> OK// You can also get the results from the list returned by// `trans.exec()`.for(Objectitem:transResults){System.out.println(item);}// >>> OKSystem.out.println(jedis.get("shellpath"));// >>> /usr/syscmds/:/usr/mycmds/}}jedis.close();}}