Relational databases generally support transactions, which, in simple terms, allow the batch execution of request submissions with a guarantee of success or failure. For Redis, it also provides simple implementation and support for transactions. See below.
Transaction implementation
Redis implements transactions through the watch, multi, exec commands. It implements a mechanism for executing a series of commands once, in sequence, with no other changes during execution.
Transaction command
watch
Data is stored
Watch monitors the state of a Key and marks it to ensure that it will not be modified by other operations during the transaction to damage the integrity of the transaction.
typedef struct redisDb {
// Omit other information
// The key being monitored by the WATCH command
dict *watched_keys; /* WATCHED keys for MULTI/EXEC CAS */
} redisDb;
Copy the code
As above, the Key about being monitored is stored inredisDb
thewatched_keys
In the dictionary array, key1 and key2 are the monitored keys, and the values in the dictionary are as followsThe liststorageredisClient
Structure references, which are mapped to indicate which clients are listening for which keys.
triggering
/* "Touch" a key, so that if this key is being WATCHed by some client the * next EXEC will fail. If the key is being monitored by some client, * then the transaction will fail when the client executes EXEC. * /
void touchWatchedKey(redisDb *db, robj *key) {
list *clients;
listIter li;
listNode *ln;
// The dictionary is empty and no keys are monitored
if (dictSize(db->watched_keys) == 0) return;
// Get all the clients monitoring this key
clients = dictFetchValue(db->watched_keys, key);
if(! clients)return;
/* Mark all the clients watching this key as REDIS_DIRTY_CAS */
/* Check if we are already watching for this key */
// Iterate over all clients and turn on their REDIS_DIRTY_CAS flag
listRewind(clients,&li);
while((ln = listNext(&li))) { redisClient *c = listNodeValue(ln); c->flags |= REDIS_DIRTY_CAS; }}Copy the code
The touchWatchedKey method is used to mark the watched_keys database by changing the flags attribute of the watched_keys object redisClient. When the watched_keys are changed, the touchWatchedKey method is used to mark the watched_keys database. Its client is marked REDIS_DIRTY_CAS, indicating that the client listening key has been modified. The touchWatchedKey method here is a bypass method that is triggered when the Redis server receives data change commands such as set, lpush, and zadd.
Transaction execution
watch
Is aOptimistic lockingWhen multiple Client clients monitor the same database Key, all monitored clients and Key mapping relationships will be saved, and the Client Client will not change the blocking or rejection of the monitoring Key in the process of unsubmitted transactions. The Client checks the transaction status only when the Client commits the transaction and rejects the transaction only when the data is changed. Otherwise, the transaction is successfully executed.
multi
Multi Starts a transaction, similar to begin in a relational database. RedisClient flags as REDIS_MULTI during execution, indicating that the client has enabled transactions.
discard
Discard Cancels transactions, similar to rollback in a relational database. The action clears the transaction queue of all enqueued commands and cancels the client transaction status.
exec
Exec performs transactions, similar to the COMMIT in a relational database. The action commits all enqueued commands in the transaction queue and cancels the client transaction status.
Execute the process
Transaction execution is divided into three phases:Transaction start, command enqueue, transaction execution (transaction discard)
- The start of a transaction indicates that the client has started a transaction
- The command teamWhen the transaction state is enabled, all subsequent execution commands are placed into one
FIFO
In the queue, the commands will not be executed and will be executed in sequence after the transaction execution command is initiated - Transaction execution When a transaction is initiated, Redis takes out the commands previously placed in the command queue and executes them in the order in which they are stored
- Transaction discarding When a transaction discarding is initiated, Redis empties the command queue and unflags the client transaction
- It is executed immediately when the Redis server receives the client’s mark that the transaction status is enabled
Multi, Watch, discard, and exec
Etc transaction command - When the Redis server receives the client marked as transaction enabled, the command is put into the transaction command queue and returned to the client
QUEUED
Indicates that the transaction command is enqueued - If not, it is not in the transaction state. Run the command normally.
The data structure
typedef struct redisClient {
// Most other information is omitted here
// Client status flag
int flags; /* REDIS_SLAVE | REDIS_MONITOR | REDIS_MULTI ... * /
// Transaction status
multiState mstate; /* MULTI/EXEC state */
// The monitored key
list *watched_keys; /* Keys WATCHED for MULTI/EXEC CAS */
} redisClient;
Copy the code
As mentioned above, redisClient is a client data structure, and Redis stores a lot of information for each client.
flags
Record the client status flag, if includedREDIS_MULTI
The flag indicates that the client is in a transactional statemstate
The record transaction state isexec
To perform ormulti
openwatched_keys
Is the key monitored after watch operation
/* * Transaction status */
typedef struct multiState {
// Transaction queue, FIFO order
multiCmd *commands; /* Array of MULTI commands */
// Queue command count
int count; /* Total number of MULTI commands */
} multiState;
Copy the code
commands
Stores a collection of commands that are enqueued in the transaction state. Here is oneFIFO
Sequential arrays are executed first.count
Log the number of commands to join the queue
/* * transaction command */
typedef struct multiCmd {
/ / parameters
robj **argv;
// Number of arguments
int argc;
// Command pointer
struct redisCommand *cmd;
} multiCmd;
Copy the code
multCmd
Stores a detailed description of the transaction command,argv
Is an array of commands stored as string objects,argc
Is the number of parameters,cmd
Point to command execution.
Exception handling
In the process of transaction execution, there are usually two kinds of errors: queue entry error and execution error.
- Enqueue error Redis will check the enqueue command, once there is an exception, it will return a prompt, when the transaction is committed, the queue command will be rejected, all commands will not be submitted
- Execution error For logical error commands, such as Key is a list object, but string object operation on it, this is mostly in the application running, but Redis in keeping it simple design philosophy, does not check the data type in the command queue stage, which can be completely avoided in the application. Unlike the enqueue error, the execution error will only fail to submit the execution of the error, and other normal commands will be submitted normally regardless of the order.
Team errors
When the transaction is not executed or a command error occurs in the queue phase, it will be prompted. When the transaction is submitted, it will be rejected directly, and the commands in the command queue will not take effect. As follows:
Perform error
The ACID to discuss
Through the above analysis of the implementation of Redis transaction mechanism, the characteristics of transaction ACID in Redis are discussed and summarized respectively.
Atomic nature
Redis can multi tag client transaction state, through the command queue to the staging all transactions during the open command, use the watch command to realize the protection for specific data based on optimistic locking implementation, in a transaction commit phase check status, check to monitor data changes the client affairs to ensure that in the process of transaction execution and submit transaction integrity, Because Redis works in a single-threaded environment, all operations are sequential, and its single-threaded design ensures that it does not have to worry about data inconsistencies due to races.
For execution errors, only correct commands can be executed correctly, while incorrect commands are ignored. This requires logical verification and fault tolerance for upper-layer applications
Isolation
Redis differs from relational databases such as MySQL in that it does not support data visibility across multiple dimensions of sessions. When not committed in the transaction state, the command will hold the command queue temporarily, while the changes in the transaction session are neither visible nor committed to the other transaction session, and the transaction commit will only actually occur during exec execution. Watch command provides a database Key Key monitoring function, it is equivalent to different multiple transactions session provides a monitoring communication’s ability to detect data changes, but it can only monitor submitted data changes to protect the transaction integrity, but can’t provide a different isolation level to MySQL to detect other uncommitted transaction session or submitted data changes.
“Durability”
The persistence of transactions in Redis depends on the opening and persistence mechanism of AOF or RDB. Even if an extreme exception occurs, as long as the AOF or RDB file is persistently flushed to disk, the transaction operation will be persistent and will be automatically loaded into memory for restoration after restart.
Consistency
Refer to the persistence section for data consistency.
To sum up the characteristics of Redis, compared with the transaction guarantee of MySQL relational database, it is still thin and simple. After all, the positioning and use scenarios of Redis are different, and the design complexity is also different. Transaction implementation is not its first priority. However, this does not affect our understanding of Redis transaction implementation mechanism and details, through the internal implementation of the scenario to make use of the transaction.
reference
Redis Design and Implementation
Github.com/huangz1990/…