目录
1. Redis 事务的概念
2. Redis 事务和 MySQL事务的区别?
3. Redis 事务常用命令
下面是在 Redis 官网上找到的关于事务的解释,这里划重点,一组命令,一个步骤。
也就是说,在客户端与 Redis 交互的时候,一个步骤中执行一组命令,它们按照顺序执行而且执行过程中不会被其他命令加塞,必须一起执行完毕,就是 Redis 事务。
比如下面这两个 set 指令,本质上来说其实是和 Redis 交互的两次,所以它们其实是两次事务。
127.0.0.1:6379> set name1 "zhangsan"
OK
127.0.0.1:6379> set name2 "lisi"
OK
(1)单独的隔离操作
Redis 事务仅仅保证事务里的操作会被连续独占的执行,Redis 命令执行是单线程架构,在执行事务内所有命令请求之前无法去执行其他客户端请求;
(2)没有隔离级别的概念
Redis 事务在提交之前任何指令都不会实际的被执行,所以不存在MySQL中脏写,脏读,不可重复读,幻读等问题;
(3)不保证原子性
Redis 事务不保证原子性,Redis 不能保证所有指令同时成功和失败,只能保证事务的指令一同执行,因为对于 Redis 来说,事务回滚带来的代价太大,影响性能;所以对于一个 Redis 事务来说,是有可能出现部分指令执行成功但部分指令执行失败的;
(4)排它性
Redis 在执行事务的过程中,能保证事务内的命令依次执行不被其他命令插入;
MULTI:标记一个事务快的开始;
EXEC:执行事务块中的所有命令;
DISCARD:取消事务,放弃执行事务块中的所有命令;
UNWATCH:取消 WATCH 命令对所有 key 的监控;
WATCH key [key...]:监控一个或多个key,如果事务执行之前这些key被其他事务改动,事务就会被打断;
示例1(事务正常提交):
使用 multi 命令开始一个事务,可以看到客户段上都标记有 "TX" 事务的标志,在陆续添加 k1,k2,k3之后,并没有显示执行成功,而是返回一个QUEUED,这是队列的意思,也就是说开启事务之后,Redis 将所有需要的命令放到了一个队列中,在执行 EXEC 操作之后,会将队列中的命令全部执行;
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) OK
3) OK
127.0.0.1:6379> get k1
"v1"
127.0.0.1:6379> get k2
"v2"
127.0.0.1:6379> get k3
"v3"
示例二(取消事务):
先存放一个 age 为24的值,然后开启事务,在使用DISCARD取消事务命令,我们再次 get age,取到的还是原来的 24,不是事务中希望添加的 20,即事务未执行。
127.0.0.1:6379> set age 20
OK
127.0.0.1:6379> mulit
(error) ERR unknown command 'mulit', with args beginning with:
127.0.0.1:6379> set age 24
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set age 20
QUEUED
127.0.0.1:6379(TX)> discard
OK
127.0.0.1:6379> get age
"24"
示例三(EXEC 之前错误,类似于编译时异常):
其实这个有点类似于 Java 中的编译时异常,就是再添加命令到事务队列中的时候命令是错的,redis 会检查出来,并将队列中的所有操作全部返回,就是不执行的意思,不是回滚,回滚是执行过但是又退回来,这里是根本没有执行,小伙伴们一定要分清楚哦
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set name jack
QUEUED
127.0.0.1:6379(TX)> set username
(error) ERR wrong number of arguments for 'set' command
127.0.0.1:6379(TX)> exec
(error) EXECABORT Transaction discarded because of previous errors.
// EXCEABORT翻译过来就是打断EXEC提交命令
127.0.0.1:6379> get name
(nil)
示例四(EXEC 之后出现错误,类似于运行时异常):
第一步,开启一个事物,存放一个数字类型的值num1和字符串的值email;
第二步,get 获取 num1 和 email ,能获取到,说明存放成功;
第三步,对num1使用 incr 命令使其自增1,对 email 也是用 incr 命令使其自增1;
从语法上来讲,所有命令本身并没有错误,所以 redis 是检查不出来的,但是在逻辑上我们不能让字符串类型的数据自增1,所以 incr email 这个命令时执行失败的,但是其他命令都没有问题,是可以正常执行的,这就是 redis 于 MySQL最大的区别,MySQL是同成功同失败,redis 则是能执行成功的就会保存不进行回滚,执行失败的那就是失败了。
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set num1 1000
QUEUED
127.0.0.1:6379(TX)> set email [email protected]
QUEUED
127.0.0.1:6379(TX)> get num1
QUEUED
127.0.0.1:6379(TX)> get email
QUEUED
127.0.0.1:6379(TX)> incr num1
QUEUED
127.0.0.1:6379(TX)> incr email
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) OK
3) "1000"
4) "[email protected]"
5) (integer) 1001
6) (error) ERR value is not an integer or out of range
补充一点:WATCH 底层采用的是CAS乐观锁,如果我们使用WATCH监控了一些 key,那么在事务中在执行修改这些被监控的 key 数据的时候,会判断当前 key 的值是否与期望值一样,如果一样就会做修改,如果不一样就会放弃修改,当前整个事务的操作都会放弃执行。