如何理解Redis中的事务

1.Redis事务

  • Redis的事务是通过multi、exec、discard和watch这四个命令来完成的。
  • Redis的单个命令都是原子性的,所以这里需要确保事务性的对象是命令集合。
  • Redis将命令集合序列化并确保处于同一事务的命令集合连续且不被打断的执行 Redis不支持回滚操作
    注意:
    1.Redis: 命令是顺序执行的,在一个事务中,有可能被执行其他客户端的命令的
    2.Redis有持久化但不保证 数据的完整性

2.事务命令

  • multi:用于标记事务块的开始,Redis会将后续的命令逐个放入队列中,然后使用exec原子化地执行这个
    命令队列
  • exec:执行命令队列
  • discard:清除命令队列
  • watch:监视key
  • unwatch:清除监视key
    以下截图展示当redis开启事务时,执行set命令时,执行结果不会立即返回,而是放到一个请求队列中:
    redis客户端1
    如何理解Redis中的事务_第1张图片
    redis客户端2
    可以看出客户端1set过的name:2 在客户端2中通过get key命令获取时是空的
    如何理解Redis中的事务_第2张图片
    以下展示在事务中如果执行了(discard:清除命令队列)那么再提交事务时会报以下错误
    如何理解Redis中的事务_第3张图片
    以下截图展示如果先在客户端1 watch 一个key,开启事务,接着set一个key:value后 ,再从客户端2从新修改了这个客户端1watch的这个key值,那么客户端1在提交事务的时候会清空队列。
    客户端1
    如何理解Redis中的事务_第4张图片
    客户端2

3.事务的执行过程

事务的执行

  1. 事务开始
    在RedisClient中,有属性flags,用来表示是否在事务中
    flags=REDIS_MULTI
  2. 命令入队
    RedisClient将命令存放在事务队列中
    (EXEC,DISCARD,WATCH,MULTI除外)
  3. 事务队列
    multiCmd *commands 用于存放命令
  4. 执行事务
    RedisClient向服务器端发送exec命令,RedisServer会遍历事务队列,执行队列中的命令,最后将执
    行的结果一次性返回给客户端。
    如果某条命令在入队过程中发生错误,redisClient将flags置为REDIS_DIRTY_EXEC,EXEC命令将会失败
    返回。
    流程图:
    如何理解Redis中的事务_第5张图片
    部分源码
typedef struct redisClient{ 
// flags 
  int flags //状态 
// 事务状态 
multiState mstate; // ..... 
}redisClient;
 // 事务状态
typedef struct multiState{ 
  // 事务队列,FIFO顺序 
  // 是一个数组,先入队的命令在前,后入队在后 
  multiCmd *commands;
   // 已入队命令数 
  int count; 
}multiState; 
   // 事务队列
typedef struct multiCmd{
   // 参数
  robj **argv; 
  // 参数数量 
  int argc; 
  // 命令指针 
 struct redisCommand *cmd;
}multiCmd;

4.Watch的执行

使用WATCH命令监视数据库键redisDb有一个watched_keys字典,key是某个被监视的数据的key,值是一个链表.记录了所有监视这个数据的客户端。监视机制的触发当修改数据后,监视这个数据的客户端的flags置为REDIS_DIRTY_CAS
事务执行
RedisClient向服务器端发送exec命令,服务器判断RedisClient的flags,如果为REDIS_DIRTY_CAS,则清空事务队列。

typedef struct redisDb{ 
// ..... 
// 正在被WATCH命令监视的键 
dict *watched_keys; 
// ..... 
}redisDb;

5.Redis的弱事务性

  • Redis语法错误
    整个事务的命令在队列里都清除
127.0.0.1:6379> multi 
OK
127.0.0.1:6379> sets m1 44 
(error) ERR unknown command `sets`, with args beginning with: `m1`, `44`, 
127.0.0.1:6379> set m2 55 
QUEUED 
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors. 127.0.0.1:6379> get m1 
 "22"

flags=REDIS_DIRTY_EXEC

  • Redis运行错误
    在队列里正确的命令可以执行 (弱事务性)
    弱事务性 :
    1、在队列里正确的命令可以执行 (非原子操作)
    2、不支持回滚
127.0.0.1:6379> multi 
OK
127.0.0.1:6379> set m1 55
QUEUED 
127.0.0.1:6379> lpush m1 1 2 3 #不能是语法错误 
QUEUED
127.0.0.1:6379> exec 
1) OK
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
127.0.0.1:6379> get m1
"55"

总结

Redis不支持事务回滚(为什么呢)
1、大多数事务失败是因为语法错误或者类型错误,这两种错误,在开发阶段都是可以预见的
2、Redis为了性能方面就忽略了事务回滚。 (回滚记录历史版本)

你可能感兴趣的:(java高级部分,java,redis,后端,缓存,数据库)