事务:
Redis的单条命令是支持事务的,但是事务不保证原子性。
Redis没有隔离级别的概念,所有的命令在事务中并没有直接去执行,只有发起执行命令的时候才执行。
Redis事务:
- 开启事务(multi)
- 命令入队(…)
- 执行事务(exec)
**Redis事务特点:**一致性,顺序性,排他性。
执行事务命令:
127.0.0.1:6379> multi #开启事务
OK
127.0.0.1:6379> set myset v1 #命令入队
QUEUED
127.0.0.1:6379> set myset v2 #命令入队
QUEUED
127.0.0.1:6379> get myset #命令入队
QUEUED
127.0.0.1:6379> set myset v3 #命令入队
QUEUED
127.0.0.1:6379> exec #执行事务
1) OK
2) OK
3) "v2"
4) OK
127.0.0.1:6379>
放弃事务命令:
127.0.0.1:6379> multi #开启事务
OK
127.0.0.1:6379> set v1 v1 #命令入队
QUEUED
127.0.0.1:6379> set v2 v2 #命令入队
QUEUED
127.0.0.1:6379> DISCARD #放弃事务
OK
127.0.0.1:6379> get v1 #取消事务后set的命令没有被执行,对象获取是不到的
(nil)
127.0.0.1:6379>
编译型异常:(代码命令有误),事务中所有的命令都不会被执行。
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set v1 v1
QUEUED
127.0.0.1:6379> set v2 v2
QUEUED
127.0.0.1:6379> getset k3 #错误的命令
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379> set v3 v3
QUEUED
127.0.0.1:6379> exec #执行事务会报错,所有的命令是不会被执行的
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379>
**运行时异常:**如果队列事务中存在语法错误,那么执行命令的时候,其他命令是可以正常执行的,错误命令则会抛出异常。
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> incr k1 #出现语法异常,字符串是不能incr的
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> get k3
QUEUED
127.0.0.1:6379> exec #执行事务时incr k1命令就会抛出异常,其他命令正常执行
1) OK
2) (error) ERR value is not an integer or out of range
3) OK
4) "v3"
127.0.0.1:6379> get k1 #能正常获取到没抛异常的对象
"v1"
127.0.0.1:6379> get k3
"v3"
127.0.0.1:6379>
所以由此看出在redis中事务是不能保证原子性的。
监控:watch
悲观锁:
- 很悲观,认为什么时候都有可能会出现问题,无论做什么都会加锁!
乐观锁:
- 很乐观,什么时候都不会出现问题,所以不会上锁!更新数据的时候判断一下在此期间是否有人修改过此数据。
- 获取version
- 更新的时候比较version
Redis监视设置:
127.0.0.1:6379> set money 1000
OK
127.0.0.1:6379> set cost 0
OK
127.0.0.1:6379> watch money #监视money对象
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 200
QUEUED
127.0.0.1:6379> incrby cost 200
QUEUED
127.0.0.1:6379> exec #事务正常结束。数据期间没有变动,则执行成功!
1) (integer) 800
2) (integer) 200
127.0.0.1:6379>
测试多客户端修改值,使用watch可以当做redis的乐观锁 操作!
客户端1监视money:
127.0.0.1:6379> set money 1000
OK
127.0.0.1:6379> set cost 0
OK
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 200
QUEUED
127.0.0.1:6379> incrby cost 200
QUEUED
客户端2修改money的数据:
27.0.0.1:6379> get money
"1000"
127.0.0.1:6379> set money 2000
OK
127.0.0.1:6379>
这时候客户端1再执行事务就会失败!
127.0.0.1:6379> set money 1000
OK
127.0.0.1:6379> set cost 0
OK
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 200
QUEUED
127.0.0.1:6379> incrby cost 200
QUEUED
127.0.0.1:6379> exec #由于客户端2修改了money的值,这时候exec就会失败!
(nil)
127.0.0.1:6379>
如果事务执行失败了就unwatch(放弃监视)先解锁,然后再次监视money,获取最新的值。
127.0.0.1:6379> set money 1000
OK
127.0.0.1:6379> set cost 0
OK
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 200
QUEUED
127.0.0.1:6379> incrby cost 200
QUEUED
127.0.0.1:6379> exec
(nil)
#以上是客户端1监视时执行失败的结果,事务执行失败就放弃监视,然后重新监视该值,
#并获取该对象最新的值
127.0.0.1:6379> unwatch #放弃监视
OK
127.0.0.1:6379> watch money #重新监视
OK
127.0.0.1:6379> get money #查看当前money的值,因为客户端2修改了money为2000
"2000"
127.0.0.1:6379> multi #开启事务
OK
127.0.0.1:6379> decrby money 300
QUEUED
127.0.0.1:6379> incrby cost 300
QUEUED
127.0.0.1:6379> exec #执行事务
1) (integer) 1700
2) (integer) 300
127.0.0.1:6379>
redis乐观锁应用场景:商品秒杀