Redis tutorial of viii: details of transactions

  • 2020-05-06 11:58:37
  • OfStack

Overview:

Like many other databases,           also provides transaction mechanism as NoSQL database. In Redis MULTI/EXEC/DISCARD/WATCH that four order is the foundation for us to achieve the transaction. Although this concept is familiar to developers with experience in relational database development, we will briefly list the transaction implementation characteristics in Redis:

          1). All commands in the transaction will be executed in serialized order. During the execution of the transaction, Redis will no longer provide any service for requests from other clients, thus ensuring that all commands in the transaction are executed atomically.

          2). Compared to a transaction in a relational database, if a command fails in an Redis transaction, the subsequent command is still executed.
          3). We can start a transaction with the MULTI command, which can be interpreted by those with experience in relational database development as "BEGIN TRANSACTION". Any command executed after this statement is treated as an operation within the transaction, and we can finally commit/roll back all operations within the transaction by executing the EXEC/DISCARD command. These two Redis commands can be considered equivalent to the COMMIT/ROLLBACK statements in a relational database.

          4). If there is a communication failure between the client and the server before the transaction is opened and the network is disconnected, the statements to be executed after that will not be executed by the server. However, if the network outage event occurs after the client executes the EXEC command, then all commands in the transaction are executed by the server.

          5). When in Append-Only mode, Redis writes all writes in the transaction to disk in this call by calling the system function write. However, if there is a system crash during the write, such as a power outage, then only part of the data may be written to disk and another part may be lost. The Redis server performs a series of necessary conformance checks when it restarts and exits with an error message if a similar problem is found. At this point, we'll take full advantage of the redis-check-aof tool provided in the Redis toolkit, which helps us locate errors with inconsistent data and roll back portions of the data that have been written. After the fix we can restart the Redis server again.

ii. List of related commands:

命令原型 时间复杂度 命令描述 返回值
MULTI   用于标记事务的开始,其后执行的命令都将被存入命令队列,直到执行EXEC时,这些命令才会被原子的执行。 始终返回OK
EXEC   执行在一个事务内命令队列中的所有命令,同时将当前连接的状态恢复为正常状态,即非事务状态。如果在事务中执行了WATCH命令,那么只有当WATCH所监控的Keys没有被修改的前提下,EXEC命令才能执行事务队列中的所有命令,否则EXEC将放弃当前事务中的所有命令。 原子性的返回事务中各条命令的返回结果。如果在事务中使用了WATCH,一旦事务被放弃,EXEC将返回NULL-multi-bulk回复。
DISCARD   回滚事务队列中的所有命令,同时再将当前连接的状态恢复为正常状态,即非事务状态。如果WATCH命令被使用,该命令将UNWATCH所有的Keys。 始终返回OK。
WATCHkey [key ...] O(1) 在MULTI命令执行之前,可以指定待监控的Keys,然而在执行EXEC之前,如果被监控的Keys发生修改,EXEC将放弃执行该事务队列中的所有命令。 始终返回OK。
UNWATCH O(1) 取消当前事务中指定监控的Keys,如果执行了EXEC或DISCARD命令,则无需再手工执行该命令了,因为在此之后,事务中所有被监控的Keys都将自动取消。 始终返回OK。

Example of :

    1. Transactions are executed normally:
   


    # in Shell Execute from the command line Redis Client tools.
    /> redis-cli
    # Starts a new transaction on the current connection.
    redis 127.0.0.1:6379> multi
    OK
    # Execute the first command in the transaction, and as you can see from the return result of that command, the command is not executed immediately, but is stored in the command queue of the transaction.
    redis 127.0.0.1:6379> incr t1
    QUEUED
    # A new command is executed, and as you can see from the result, it is also stored in the command queue of the transaction.
    redis 127.0.0.1:6379> incr t2
    QUEUED
    # Execute all the commands in the transaction command queue, and as you can see from the results, the results of the commands in the queue are returned.
    redis 127.0.0.1:6379> exec
    1) (integer) 1
    2) (integer) 1
  
     
    2. There is a failed command in the transaction:
   

    # Start a new transaction.
    redis 127.0.0.1:6379> multi
    OK
    # Set the key a The value of string The type of 3 .
    redis 127.0.0.1:6379> set a 3
    QUEUED
    # From the key a The header of the associated value pops up the element, because the value is of string type, and lpop Commands can be used only List Type and therefore in execution exec When you command, the command will fail.
    redis 127.0.0.1:6379> lpop a
    QUEUED
    # Set the key again a Is a string 4 .
    redis 127.0.0.1:6379> set a 4
    QUEUED
    # Access to key a In order to confirm whether the value is the second in the transaction set Command setup successful.
    redis 127.0.0.1:6379> get a
    QUEUED
    # As you can see from the result, the second command in the transaction lpop The execution failed, and the following set and get The commands were executed successfully at this point Redis The most important difference between transactions in a relational database and transactions in a relational database.
    redis 127.0.0.1:6379> exec
    1) OK
    2) (error) ERR Operation against a key holding the wrong kind of value
    3) OK
    4) "4"

    3. Rollback transaction:
   

    # As the key t2 Sets the value before a transaction is executed.
    redis 127.0.0.1:6379> set t2 tt
    OK
    # Opens a transaction.
    redis 127.0.0.1:6379> multi
    OK
    # Sets a new value for the key within the transaction.
    redis 127.0.0.1:6379> set t2 ttnew
    QUEUED
    # Discard transactions.
    redis 127.0.0.1:6379> discard
    OK
    # Check the key t2 From the result, you can see that the value of this key is still the value before the transaction starts.
    redis 127.0.0.1:6379> get t2
    "tt"

iv, WATCH commands and optimistic locks based on CAS:

In Redis transactions, the WATCH command can be used to provide CAS(check-and-set) functionality. Suppose we monitor multiple Keys before the transaction is executed through the WATCH command. If any Key value changes after WATCH, the transaction executed by the EXEC command will be abandoned, and the Null multi-bulk reply will be returned to notify the caller that the transaction failed. For example, let's assume again that the incr command is not provided in Redis to perform the atomic increment of key values, and that we have to write our own code to do so. Its pseudo-code is as follows:
   


      val = GET mykey
      val = val + 1
      SET mykey $val
  

The code above           can only be guaranteed to execute correctly if there is a single connection, because if there are multiple clients executing the code at the same time, an error scenario that often occurs in multithreaded programs -- race (race condition). For example, the clients A and B both read the original value of mykey at the same time, assuming that the value is 10. After that, both clients add the value to set back to Redis server, which results in mykey being 11 instead of 12. To solve this problem, we need the help of the WATCH command, which is shown in the following code:
   

      WATCH mykey
      val = GET mykey
      val = val + 1
      MULTI
      SET mykey $val
      EXEC
  

          unlike the previous code, the new code before obtaining mykey value through WATCH command to monitor the key, and then will set commands surrounded in the transaction, so that it can effectively guarantee each connection before execution EXEC, if the value of the current connection to obtain mykey modified by other connected client, then the current connection EXEC command will failure. This lets the caller know if val has been reset successfully after determining the return value.


Related articles: