Redis core principles and practice-transaction practice and source code analysis

> MULTI
OK
> Set Points 1
Queued
> Incr points
Queued
> Exec
1) (Integer) 1
2) (Integer) 1

TypeDef Struct Multistate {
multicmd *Commands;
Em
} Multistate;

void watchforKey (client *c, robj *key) {
Em
// [1]
Clients = DictfetchValue (C-> DB-> Watched_keys, Key);
Em
listAddnodetail (clients, c);

// [2]
wk = zmalloc (sizeof (*wk));
wk-> key = key;
wk-> db = c-> db;
incrrefcount (key);
listadDNODETail (C-> Watched_keys, WK);
}

Void TouchWatchedkey (Redisdb *DB, ROBJ *Key) {
Em
Clients = DictfetchValue (db-> watchd_keys, key);
if (! Clients) Return;

listrewind (clients, & li);
While ((ln = listnext (& li)) {{
client *c = listNodeValue (ln);

C-> Flags | = Client_dirty_cas;
}
}

int ProcessCommand (client *c) {
Em
if (C-> Flags & Client_multi &&
C-> CMD-> PROC! = EXECCOMMAND && C-> CMD-> PROC! = DiscardCommand &&
C-> CMD-> PROC! = Multicommand && C-> CMD-> PROC! = WatchCommand)
{{
queuemulticommand (C);
addreply (c, shared.queuered);
} …
Return C_ok;
}

void execcommand (client *c) {
Em

// [1]
if (C-> Flags & (Client_dirty_cas | Client_dirty_exec) {{
addreply (C, C-> Flags & Client_Dirty_exec? Shared.execaborterr: Shared.nullarray [C-> Resp]);
Discardtransaction (C);
Goto handle_monitor;
}

// [2]
unwatchallkeys (C);
Em
addreplyarraylen (C, C-> MSTATE.COUNT);
for (j = 0; j mstate.count; j ++) {
c-> argc = c-> mstate.commands [j] .argc;
c-> argv = c-> mstate.commands [j] .argv;
C-> cmd = c-> mstate.commands [j] .cmd;

// [3]
if (! Must_propagate &&
! Server.loading &&
! (C-> CMD-> Flags & (CMD_READONL | CMD_ADMIN))))
{{
ExecocommandpropagateMulti (C);
Must_propagate = 1;
}
// [4]
int ACL_KEYPOS;
int ACL_RETVAL = ACLCHECKCOMMANDPERM (C, & ACL_KEYPOS);
if (ACL_RETVAL! = ACL_OK) {
Em
} else {
call (c, server.loading? cmd_call_none: cmd_call_full);
}
Em
}
// [5]
Em
Discardtransaction (C);

// [6]
if (Must_propagate) {
int is_master = server.masterhost == null;
Server.dirty ++;
Em
}
Em
}

Redis supports transaction mechanism, but Redis’s transaction mechanism is different from the traditional relationship database.

The essence of redis transactions is a set of commands (command queues). Affairs can execute multiple commands at one time and provide the following guarantees:

(1) All commands in the transaction are executed in order. During the execution of the transaction command, the command requests submitted by other clients need to wait for the execution of all the commands of the current office before processing, and will not be inserted into the current transaction command queue.

(2) The commands in the transaction are either executed or not executed. Even if there are some commands in the transaction failed, the subsequent commands are still executed. Therefore, redis transactions are also atoms.

Note that Redis does not support rollback. If the command in the transaction fails, then Redis will continue to execute the follow -up command instead of rolling.

Maybe readers wondered whether Redis supports ACID? The author believes that the concept of ACID originated from a traditional relationship database, and Redis is a non -relational database, and Redis does not declare whether it supports ACID, so this article does not discuss the problem.

Example of transaction application

Redis provides Multi, EXEC, Discard, and Watch commands to implement transaction functions:

The Multi command can turn on a transaction, and the subsequent commands will be put into the transaction command queue.

The exec command can execute all the commands in the transaction command queue. The Discard command can abandon the command in the transaction command queue. Both commands will end the current transaction.

The Watch command can monitor the specified key. When these keys have been modified before the follow -up transaction is executed, the transaction is rejected.

Table 17-1 shows a simple example of a Watch command.

It can be seen that if the Watch key is modified before executing the EXEC command, the EXEC command will not execute transactions, so Watch is often used to achieve optimistic locks.

Principles of transaction implementation

Server.H/Multistate structure is responsible for storing transaction information:

Commands: transaction command queue to store all commands in the current transaction.

The client attribute client.mState points to a Multistate variable. The Multistate is the context of the client’s transaction, which is responsible for storing the current transaction information of the client.

Let’s take a look at the implementation of Multi, Exec, and Watch commands.

Implementation of the Watch command

Tip: If there is no special instructions in this chapter, it is in Multi.c.

Redis核心原理与实践--事务实践与源码分析

The implementation of the Watch command is more independent, and we first analyze the implementation logic of the command.

The redisdb defines the dictionary attribute Watched_keys. The key of the dictionary is the Redis key monitored in the database. The value of the dictionary is the list of all clients of the dictionary key, as shown in Figure 17-1.

Client also defines the list attributes watched_keys to record all the monitoring keys of the client.

WatchCommand function is responsible for handling the Watch command. This function will call the WatchForKey function processing related logic:

[1] Add the client to the client list corresponding to the redis key in the redisdb.watched_keys dictionary.

[2] Initialize the WATCHEDKEY structure (WK variable), which can store the monitoring key and the corresponding database. Add WK variables to client.watched_keys.

Every time Redis modifies the data, the SignalModifiedKey function calls the data logo as modified.

The SignalModifiedKey function will call the TouchWATCHEDKEY function to inform the client data of the key to monitor the key has been modified:

Obtain all clients that monitor the key from Redisdb.WZATCHED_KEYS, add Client_ Dirty_cas logo to these clients, which represents the client monitoring keys.

Multi, exec command implementation

The Multi command is processed by the Multicommand function. The processing of this function is very simple, that is, the client client_multi logo is opened, which means that the client has opened the transaction.

As mentioned earlier, when the ProcessCommand function executes commands, the client will check whether the client has turned on transactions. If the client has turned on the transaction, call the QueueMulticommand function, add the command request to the client transaction command queue client.mState.commands:

It can be seen that if the current client is turned on, except for Multi, EXEC, Discard, and Watch commands, other commands will be placed in the transaction command queue.

Execal command is processed by the Execocommand function:

[1] When the client monitoring key is modified (client_dirty_cas logo) or the client’s command (client_dirty_exec logo) is directly abandoned in the transaction order queue, and error treatment is processed.

When the server is in an abnormal state (such as memory overflow), Redis will refuse the command and add client_dirty_exec logo to the client that starts the transaction.

[2] Cancel the current client’s monitoring of all keys, so the Watch command can only act on one subsequent transaction.

[3] Before the first writing command of the transaction, spread the Multi command to the AOF file and from the node. After the Multi command is executed, it will not be transmitted (the Multi command does not belong to the writing command). If the writing command is executed in the transaction, the Multi command is spread here.

[4] Check the user’s ACL permissions, and execute the command after checking.

[5] After executing all the commands, call the DiscardtransAction function to reset client’s transaction context client.mState, and delete client_multi, client_dirty_cas, client_dirty_exec logo, which means that the current transaction has been handled.

[6] If the writing command is executed in the transaction, modify the server.dirty, which will make the server.c/call function spread the exec command to the AOF file and from the node, thereby ensuring that the Multi and Exec commands of a transaction are spread.

Regarding Redis does not support the rollback mechanism, Redis gave the following explanation on the official website:

(1) Only when the error syntax (and the error cannot be detected during the command to join the queue) or the data type error of the data type of the Redis command (such as using the HGET command for the collection type), the command execution in the transaction failed, and the execution of the transaction failed.This means that the failure of failure in the affairs is the result of programming errors, so these problems should be found and dealt with during the development process, rather than relying on the rollback mechanism in the production environment to avoid.

(2) Do not support the rollback, the Redis transaction mechanism is simpler and more performance.

Redis’s transaction is very simple, that is, multiple commands are executed in an atomic operation.Redis’s LUA script is also transactional, so users can also use the LUA script to implement transactions.The Redis Lua script will be analyzed in detail in the subsequent chapters.

Summarize:

Redis核心原理与实践--事务实践与源码分析

Redis affairs guarantee multiple commands in an atomic operation.

Redis provides Multi, EXEC, Discard, and Watch commands to achieve transaction functions.

Using the Watch command to achieve an optimistic lock mechanism.

You may also like...

Popular Posts