Redis事务篇
Redis事务相比mysql数据库比较简单,事务共有三个主要的命令,multi(用于开启事务),exec(用于执行事务),discard(用于抛弃事务);
Redis事务执行的流程:
1。执行multi命令开启一个事务;
2.申明一系列指令,这时Redis-server收到指令后不会立即执行事务中的命令,而是放入一个事务指令的队列中等待客户端发送exec指令;
3.执行exec指令,当Redis-server接收到exec指令后就会开始执行事务队列中的指令,由于Redis是单线程
所以可以保证事务执行的隔离性和相对的原子性;(或者执行discard命令使得Redis-server抛弃事务队列中的命令)
> MULTI
OK
> INCR foo
QUEUED
> INCR bar
QUEUED
> EXEC
1) (integer) 1
2) (integer) 1
Redis事务的原子性并不是真正的原子性:
当在事务中的命令有语法上的错误时,整个事务会被discard;
但是到了执行exec命令之前并没有报错,执行exec命令后Redis-server开始执行事务中的命令,这时如果事务中的命令有误,整个事务并不会回滚,只有错误的命令不会执行,而正确的命令会被执行;(有一个小细节,当执行事务中redis-server突然挂了,这时事务可能刚执行到一半,但是aof已经记录了该事务的部分命令,可能会导致redis-server重启的时候报错,可以使用Redis-check-aof工具修复;Redis不支持回滚是因为Redis想保存Redis的高性能和简单;假设一下如果要支持事务需要做些什么,在mysql里是将修改的命令记录在undo.log中,当需要回滚时就执行undo.log中的回滚操作;
由于Redis的事务和一般的数据库的事务有区别:事务的命令会放入在Redis-server的事务队列中,直到执行exec命令时才会执行所有的事务命令,所以有这样的场景:在Redis有一个number的整数值,现在需要将它乘n再加上m,但是Redis并没有直接提供乘法的命令,所以我们一般需要将number读到客户端,然后进行算术运算,在通过set 命令更新值;但是由于在一个事务过程中我们是没法得到number的值(因为命令直到exec才开始执行命令),这就需要在事务执行前就需要通过get命令得到值,然后在set;但是这样显然是违背了数据并发执行过程中的数据安全;所以Redis提供了一个watch命令,这个命令具有CAS(check and set)功能;我们先展示一下使用方式吧:
mset n1 1 n2 2
watch n1 n2
init n1=get(n1);
int n2=get(n2);
multi
set(n1+n2)
set(n1+n2)
exec
我们一般会在一个事务开启之前用watch命令盯住一个或者多个变量,如果在这个事务执行exec命令之前有任何客户端更改了watched中的任何一个变量Redis-server都会抛弃这个事务;注意如果即使是执行watch命令的客户端如果在watch命令和multi命令之间对watched变量进行修改都会报错;
最后返回null就是抛弃事务的意思;在Redis版本为 6.0.9之前,Redis-server自身的修改(过期)不会造成事务被抛弃,在6.0.9之后就有了这个功能,如下