[Redis] 5. Redis transaction processing

Keywords: Java Redis Jedis Programming

MULTI, EXEC, DISCARD and WATCH are the basis of Redis transactions

1. The MULTI command is used to open a transaction and always returns OK.
After MULTI is executed, the client can continue to send any number of commands to the server, which will not be executed immediately, but will be placed in a queue.

2. When the EXEC command is invoked, all the commands in the queue are executed.

 

+++++++++ ++ Command +++++++++++++++++++

redis 192.168.1.53:6379> multi
OK
redis 192.168.1.53:6379> incr foo
QUEUED
redis 192.168.1.53:6379> set t1 1
QUEUED
redis 192.168.1.53:6379> exec
1) (integer) 2
2) OK

+++++++++ java code corresponding to +++++++++++++++++++

Jedis jedis = new Jedis("192.168.1.53", 6379);
Transaction tx = jedis.multi();
tx.incr( "foo");
tx.set( "t1", "2");
List<Object> result = tx.exec();

if (result == null || result.isEmpty()) {
     System. err.println( "Transaction error...");
     return ;
}

for (Object rt : result) {
     System. out.println(rt.toString());
}

Two types of errors may occur when using transactions:

1. Before the transaction executes EXEC, the entry order may be wrong. For example, commands may produce grammatical errors (parameter number errors, parameter name errors, etc.) or other more serious errors, such as insufficient memory.

(If the server uses maxmemory to set the maximum memory limit).

2. The command may fail after the EXEC call. For example, commands in a transaction might handle key types of errors, such as using list commands on string keys, and so on.

The first mistake is:
Server side:
Prior to Redis 2.6.5, Redis only executed the successful entry commands in the transaction, ignoring the failed entry commands.

However, starting with Redis 2.6.5, the server records the failure of command entry and refuses to execute and automatically abandons the transaction when the client invokes the EXEC command.

+++++++++ ++ Command +++++++++++++++++++

 

redis 192.168.1.53:6379> multi
OK
redis 192.168.1.53:6379> incr foo
QUEUED
redis 192.168.1.53:6379> set ff 11 22
(error) ERR wrong number of arguments for 'set' command
redis 192.168.1.53:6379> exec
1) (integer) 4

Because my version is: 2.6.4, Redis only executes the successful entry commands in the transaction and ignores the failed entry commands.

Client (jredis):

The client's previous practice was to check the return value of command entry: if the command returns QUEUED when it enters the queue, then the entry succeeds; otherwise, the entry fails. If a command fails to enter the team,

Most clients then stop and cancel the transaction.

 

The second mistake is:

As for the errors that occur after the EXEC command is executed, they are not specially handled: even if one or some of the commands in the transaction are executed with errors, the other commands in the transaction will continue to execute.

+++++++++ ++ Command +++++++++++++++++++++

redis 192.168.1.53:6379> multi
OK
redis 192.168.1.53:6379> set a 11
QUEUED
redis 192.168.1.53:6379> lpop a
QUEUED
redis 192.168.1.53:6379> exec
1) OK
2) (error) ERR Operation against a key holding the wrong kind of value

+++++++++ java code corresponding to +++++++++++++++++++

 

Jedis jedis = new Jedis("192.168.1.53", 6379);
Transaction tx = jedis.multi();
tx.set( "t1", "2");
tx.lpop( "t1");
List<Object> result = tx.exec();

if (result == null || result.isEmpty()) {
    System. err.println( "Transaction error...");
    return ;
}

for (Object rt : result) {
    System. out.println(rt.toString());
}

Redis does not roll back when a transaction fails, but continues to execute the remaining commands

This approach may seem a little strange to you. Here are the advantages of this approach:
1. The Redis command will only fail because of the wrong grammar (and these problems can't be found when entering the queue), or the command is used on the wrong type of key: that is to say, from a practical point of view,

Failed commands are caused by programming errors that should be discovered during development rather than in production environments.
2. Because there is no need to support rollback, Redis's internals can be kept simple and fast.

 

Given that there is no mechanism to avoid programmers'own errors and that such errors usually do not occur in production environments, Redis chose a simpler and faster rollback-free approach to transactions.

 

3. When DISCARD command is issued, the transaction will be abandoned, the transaction queue will be emptied, and the client will exit from the transaction status.

+++++++++ ++ Command +++++++++++++++++++

 

redis 192.168.1.53:6379> set foo 1
OK
redis 192.168.1.53:6379> multi
OK
redis 192.168.1.53:6379> incr foo
QUEUED
redis 192.168.1.53:6379> discard
OK
redis 192.168.1.53:6379> get foo
"1"

4.WATCH command can provide check-and-set (CAS) behavior for Redis transactions
The keys that are WATCH are monitored and whether they have been changed. If at least one of the monitored keys is modified before EXEC executes, the entire transaction will be cancelled.

+++++++++ ++ First command +++++++++++++++++++

 

redis 192.168.1.53:6379> watch foo
OK
redis 192.168.1.53:6379> set foo 5
OK
redis 192.168.1.53:6379> multi
OK
redis 192.168.1.53:6379> set foo 9
QUEUED

+++++++++ ++ pause (executing the second command before executing the following) ++++++++++++++++++++++++++++++++++++++++++++++.

redis 192.168.1.53:6379> exec
(nil)
redis 192.168.1.53:6379> get foo
"8"

+++++++++ +++++++++++++++++++++ is the second command of +++.

redis 192.168.1.53:6379> set foo 8
OK

+++++++++ java code corresponding to +++++++++++++++++++

 

Jedis jedis = new Jedis("192.168.1.53", 6379);
jedis.watch( "foo");
Transaction tx = jedis.multi();
tx.incr( "foo");

List<Object> result = tx.exec();          //Runtime interrupts here, and then changes the foo value from the command line
if (result == null || result.isEmpty()) {
    System. err.println( "Transaction error...");
     return;
}
for (Object rt : result) {
     System. out.println(rt.toString());
}

If after WATCH is executed and before EXEC is executed, other clients change the value of mykey, then the current client's transaction will fail. What the program needs to do is try this operation again and again until no collision occurs.
This form of lock is called optimistic lock. It is a very powerful lock mechanism. And because in most cases, different clients access different keys and collisions are rare, there is usually no need to retry.

Posted by thomasd1 on Sun, 07 Jul 2019 19:03:02 -0700