一、前言
上一篇文章总结了redis事务的使用,今天分享redis的原理;
二、概念
redis支持事务机制,但是redis的事务机制与传统关系型数据库的事务机制并不相同。
redis事务的本质是一组命令的集合(命令队列)。事务可以一次执行多个命令,并提供以下保证:
1)事务中的所有命令都按顺序执行。事务命令执行过程中,其他客户端提交的命令请求需要等待当前事务所有命令执行完成后再处理,不会插入当前事务命令队列中;
2)事务中的命令要么都执行,要么都不执行,即使事务中有些命令执行失败,后续命令依然被执行。因此redis事务也是原子的。
注意:redis不支持回滚,如果事务中有命令执行失败了,那么redis会继续执行后续命令而不是回滚。
三、redis事务命令
watch命令可以监听指定键,当后续事务执行前发现这些键已修复时,则拒绝执行事务;
multi命令可以开启一个事务,后续的命令都会被放入事务命令队列;
exec命令可以执行事务命令队列中的所有命令
discard命令可以抛弃事务命令队列中的命令,和exec命令都会结束当前事务;
四、执行示例
1)在事务执行过程中,没有其它客户端执行时
127.0.0.1:6379> set score 1
OK
127.0.0.1:6379>
127.0.0.1:6379>
127.0.0.1:6379> watch score
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr score
QUEUED
127.0.0.1:6379> exec
1) (integer) 2
127.0.0.1:6379> get score
"2"
可以看到exec的执行结果为:(integer)2
2)在事务执行过程中,有其它客户端修改对应key的值

客户端client2在客户端client1执行完incr score命令之后执行set score 10,可以看到exec执行结果为false,说明client1的事务取消执行
五、事务的实现原理
1、server.sh/multiState结构体负责存放事务信息:
typedef struct multiState {
multiCmd *commands; /* Array of MULTI commands */
int count; /* Total number of MULTI commands */
int cmd_flags; /* The accumulated command flags OR-ed together.
So if at least a command has a given flag, it
will be set in this field. */
int cmd_inv_flags; /* Same as cmd_flags, OR-ing the ~flags. so that it
is possible to know if all the commands have a
certain flag. */
int minreplicas; /* MINREPLICAS for synchronous replication */
time_t minreplicas_timeout; /* MINREPLICAS timeout as unixtime. */
} multiState;
commands:事务命令队列,存放当前事务所有的命令。
客户端属性client.mstate指向一个multiState变量,该multiState作为客户端的事务上下文,负责存放该客户端当前的事务信息。
2、watch命令
redisDb中定义了字典属性watched_keys,该字典的键是数据库中被监视的redis键,字典的值是监视字典键的所有客户端列表;
watchCommand函数负责处理watch命令,该函数会调用watchForKey函数处理相关逻辑:
/* ===================== WATCH (CAS alike for MULTI/EXEC) ===================
*
* The implementation uses a per-DB hash table mapping keys to list of clients
* WATCHing those keys, so that given a key that is going to be modified
* we can mark all the associated clients as dirty.
*
* Also every client contains a list of WATCHed keys so that's possible to
* un-watch such keys when the client is freed or when UNWATCH is called. */
/* In the client->watched_keys list we need to use watchedKey structures
* as in order to identify a key in Redis we need both the key name and the
* DB */
typedef struct watchedKey {
robj *key;
redisDb *db;
} watchedKey;
/* Watch for the specified key */
void watchForKey(client *c, robj *key) {
list *clients = NULL;
listIter li;
listNode *ln;
watchedKey *wk;
/* Check if we are already watching for this key */
listRewind(c->watched_keys,&li);
while((ln = listNext(&li))) {
wk = listNodeValue(ln);
if (wk->db == c->db && equalStringObjects


654

被折叠的 条评论
为什么被折叠?



