一、redis事务介绍
Redis中的事务(transaction)是一组命令的集合。事务同命令一样都是Redis最小的执行单位,一个事务中的命令要么都执行,要么都不执行。Redis事务的实现需要用到 MULTI 和 EXEC 两个命令,事务开始的时候先向Redis服务器发送 MULTI 命令,然后依次发送需要在本次事务中处理的命令,最后再发送 EXEC 命令表示事务命令结束。
二、Demo
1.正常执行的情况
127.0.0.1:6379> MULTI OK 127.0.0.1:6379> set k1 v1 QUEUED 127.0.0.1:6379> set k2 v2 QUEUED 127.0.0.1:6379> set k3 v3 QUEUED 127.0.0.1:6379> EXEC 1) OK 2) OK 3) OK 127.0.0.1:6379> keys * 1) "k2" 2) "k3" 3) "k1"
2.取消执行的情况
127.0.0.1:6379> MULTI OK 127.0.0.1:6379> set k4 v4 QUEUED 127.0.0.1:6379> set k5 v5 QUEUED 127.0.0.1:6379> DISCARD OK 127.0.0.1:6379> keys * 1) "k2" 2) "k3" 3) "k1"
我们可以看到,当执行DISCARD指令时,k4和k5没有存入内存
3.redis事务与mysql事务的不同点
3.1 非原子性表现
mysql事务内的sql是一组原子操作,要么事务内的多条sql都执行成功,要么事务内的多条sql都执行失败;但是redis事务不是原子性的,如下:虽然k2的值是v2,incr k2这个命令会失败,但是,set k4 v4 和 get k3 这两个命令还是成功了
127.0.0.1:6379> keys * 1) "k2" 2) "k1" 3) "k3" 127.0.0.1:6379> MULTI OK 127.0.0.1:6379> set k4 v4 QUEUED 127.0.0.1:6379> get k3 QUEUED 127.0.0.1:6379> INCR k2 QUEUED 127.0.0.1:6379> EXEC 1) OK 2) "v3" 3) (error) ERR value is not an integer or out of range 127.0.0.1:6379> get k4 "v4" 127.0.0.1:6379>
3.2 原子性表现,当指令入队列失败时,redis的事务表现为原子性,如下,set k5 v5 指令没有成功
127.0.0.1:6379> keys * 1) "k4" 2) "k2" 3) "k1" 4) "k3" 127.0.0.1:6379> MULTI OK 127.0.0.1:6379> set k5 v5 QUEUED 127.0.0.1:6379> set k6 (error) ERR wrong number of arguments for 'set' command 127.0.0.1:6379> EXEC (error) EXECABORT Transaction discarded because of previous errors. 127.0.0.1:6379> get k5 (nil) 127.0.0.1:6379>
4.由于redis事务非原子性,所以当一组命令一定要原子性操作时,我们可以使用WATCH key指令,来监控一组事务中,要操作的key,从而保证数据的完整性,如下,假设,一张银行信用卡,我们设置,可刷余额为 100元,我们的欠额为0元,当我们刷卡操作时,监控balance
4.1正常执行情况:
127.0.0.1:6379> set balance 100 OK 127.0.0.1:6379> set debt 0 OK 127.0.0.1:6379> WATCH balance OK 127.0.0.1:6379> MULTI OK 127.0.0.1:6379> DECRBY balance 20 QUEUED 127.0.0.1:6379> INCRBY debt 20 QUEUED 127.0.0.1:6379> EXEC 1) (integer) 80 2) (integer) 20 127.0.0.1:6379> get balance "80" 127.0.0.1:6379> get debt "20" 127.0.0.1:6379>
4.2 当redis事务还没有提交时,我们监控的key被改动时,事务会提交失败
127.0.0.1:6379> get balance "80" 127.0.0.1:6379> get debt "20"127.0.0.1:6379> WATCH balance OK 127.0.0.1:6379> set balance 500 #假设,监控了balance,事务还没开启或开启了,没有提交的情况下,balance被更改,事务将会提交失败 OK 127.0.0.1:6379> MULTI OK 127.0.0.1:6379> DECRBY balance 20 QUEUED 127.0.0.1:6379> INCRBY debt 20 QUEUED 127.0.0.1:6379> EXEC (nil) 127.0.0.1:6379> get balance "500" 127.0.0.1:6379> get debt "20" 127.0.0.1:6379>