Redis 事务
2015-08-01 17:14
603 查看
redis 对事务的支持目前还比较简单。redis 只能保证一个 client 发起的事务中的命令可以连续的执行,而中间不会插入其他 client 的命令。 由于 redis 是单线程来处理所有 client 的请求的所以做到这点是很容易的。一般情况下 redis 在接受到一个 client 发来的命令后会立即处理并 返回处理结果,但是当一个 client 在一个连接中发出 multi 命令有,这个连接会进入一个事务上下文,该连接后续的命令并不是立即执行,而是先放到一个队列中。当从此连接受到
exec 命令后,redis 会顺序的执行队列中的所有命令。并将所有命令的运行结果打包到一起返回给 client.然后此连接就 结束事务上下文。
遍历事务队列中的每个项,
读取命令参数,参数的个数,以及要执行的命令,
执行命令,并将命令的返回值追加到回复队列末尾,
移除REDIS_MULTI标识,让客户端返回非事务状态,
清空客户端的事务状态,包括入队命令计数器和释放事务队列。
2.命令可能在 EXEC 调用之后失败。举个例子,事务中的命令可能处理了错误类型的键,比如将列表命令用在了字符串键上面,诸如此类。
对于发生在 EXEC 执行之前的错误,客户端以前的做法是检查命令入队所得的返回值:如果命令入队时返回QUEUED,那么入队成功;否则,就是入队失败。如果有命令在入队时失败,那么大部分客户端都会停止并取消这个事务。
从 Redis 2.6.5 开始,服务器会对命令入队失败的情况进行记录,并在客户端调用 EXEC 命令时,拒绝执行并自动放弃这个事务。至于那些在 EXEC 命令执行之后所产生的错误, 并没有对它们进行特别处理: 即使事务中有某个/某些命令在执行时产生了错误, 事务中的其他命令仍然会继续执行。
每个redis数据库中都保存一个watched_keys字典,字典的键是被监视的键,字典的值是一个链表,链表中记录了所有监视相应数据库的客户端。
所有对数据库进行修改的命令,执行后都会调用touchWatchKey函数对watched_keys字典进行检查,查看客户端正在监视刚被命令修改过的数据库键,如果有,该函数将被修改键的客户端的flags标识REDIS_DIRTY_CAS标识打开,表示事务安全性已经被破坏。
事务安全判断:
Jedis - Redis事务的实现
乐观锁示例
exec 命令后,redis 会顺序的执行队列中的所有命令。并将所有命令的运行结果打包到一起返回给 client.然后此连接就 结束事务上下文。
eg: MULTI OK INCR foo QUEUED INCR bar QUEUED EXEC 1) :1 2) :1
事务开始:
MULTI命令标志着事务开始,当客户端发送该命令时,客户端状态的flags属性打开REDIS_MULTI标识。命令入队:
事务队列:
每个客户端都有自己的事务状态,这个事务状态保存在客户端状态的mstate属性:typedef struct redisClient { ... // 事务状态 multiState mstate; /* MULTI/EXEC state */ }事务包含一个事务队列,以及已入队命令的计数器:
/* * 事务状态 */ typedef struct multiState { // 事务队列,FIFO 顺序 multiCmd *commands; /* Array of MULTI commands */ // 已入队命令计数 int count; /* Total number of MULTI commands */ } multiState;事务队列是一个multiCmd类型的数组,数组中的每一个multiCmd都保存了一个已入队命令的相关信息:
/* * 事务命令 */ typedef struct multiCmd { // 参数 robj **argv; // 参数数量 int argc; // 命令指针 struct redisCommand *cmd; } multiCmd;
/* * Redis 命令 */ struct redisCommand { // 命令名字 char *name; // 实现函数 redisCommandProc *proc; // 参数个数 int arity; // 字符串表示的 FLAG char *sflags; /* Flags as string representation, one char per flag. */ // 实际 FLAG int flags; /* The actual flags, obtained from the 'sflags' field. */ /* Use a function to determine keys arguments in a command line. * Used for Redis Cluster redirect. */ // 从命令中判断命令的键参数。在 Redis 集群转向时使用。 redisGetKeysProc *getkeys_proc; /* What keys should be loaded in background when calling this command? */ // 指定哪些参数是 key int firstkey; /* The first argument that's a key (0 = no keys) */ int lastkey; /* The last argument that's a key */ int keystep; /* The step between first and last key */ // 统计信息 // microseconds 记录了命令执行耗费的总毫微秒数 // calls 是命令被执行的总次数 long long microseconds, calls; };举例:
事务执行:
创建空白回复队列,遍历事务队列中的每个项,
读取命令参数,参数的个数,以及要执行的命令,
执行命令,并将命令的返回值追加到回复队列末尾,
移除REDIS_MULTI标识,让客户端返回非事务状态,
清空客户端的事务状态,包括入队命令计数器和释放事务队列。
使用事务时可能会遇上以下两种错误:
1.事务在执行 EXEC 之前,入队的命令可能会出错。比如说,命令可能会产生语法错误(参数数量错误,参数名错误,等等),或者其他更严重的错误,比如内存不足(如果服务器使用 maxmemory 设置了最大内存限制的话)。2.命令可能在 EXEC 调用之后失败。举个例子,事务中的命令可能处理了错误类型的键,比如将列表命令用在了字符串键上面,诸如此类。
对于发生在 EXEC 执行之前的错误,客户端以前的做法是检查命令入队所得的返回值:如果命令入队时返回QUEUED,那么入队成功;否则,就是入队失败。如果有命令在入队时失败,那么大部分客户端都会停止并取消这个事务。
从 Redis 2.6.5 开始,服务器会对命令入队失败的情况进行记录,并在客户端调用 EXEC 命令时,拒绝执行并自动放弃这个事务。至于那些在 EXEC 命令执行之后所产生的错误, 并没有对它们进行特别处理: 即使事务中有某个/某些命令在执行时产生了错误, 事务中的其他命令仍然会继续执行。
WATCH命令:
监视数据库键:每个redis数据库中都保存一个watched_keys字典,字典的键是被监视的键,字典的值是一个链表,链表中记录了所有监视相应数据库的客户端。
typedef struct redisDb { ... // 正在被 WATCH 命令监视的键 dict *watched_keys; /* WATCHED keys for MULTI/EXEC CAS */ }监视机制触发:
所有对数据库进行修改的命令,执行后都会调用touchWatchKey函数对watched_keys字典进行检查,查看客户端正在监视刚被命令修改过的数据库键,如果有,该函数将被修改键的客户端的flags标识REDIS_DIRTY_CAS标识打开,表示事务安全性已经被破坏。
事务安全判断:
Jedis - Redis事务的实现
乐观锁示例
相关文章推荐
- Redis Sentinel初体验
- 小贝_redis web管理界面工具安装
- 小贝_redis安装与部署
- 小贝_redis学习目标
- Redis 数据持久化
- Jedis - Redis通信协议
- Twemproxy redis 配置(session和db)
- Redis 通信协议
- 生产环境下Redis主备配置(持久化)
- Redis持久化实践及灾难恢复模拟
- 消息队列 redis vs nsq
- redis 学习
- redis 安装与部署
- redis web管理界面工具安装
- Redis数据类型之字典
- redis学习小结
- redis --初级笔记
- redis3.0.3集群安装详细步骤
- Redis安装和简单测试
- redis改密码