【redis】redis事物和锁
1、redis事物
1.1、redis事物的特性
一组命令的集合!一个事物中的所有命令都会被序列化,在事物执行过程中按照顺序执行。
具有一致性、隔离性、持久性,但是没有原子性
redis单调命令保证原子性,但是事物没有原子性
redis的事物
- 开启事物(multi)
- 命令入队(...)
- 执行事物(exec)
1.2、事物实现
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> get k1
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> exec #提交事物
1) OK
2) OK
3) "v1"
4) OK
127.0.0.1:6379> set k1 v2
QUEUED
127.0.0.1:6379> discard #放弃修改
OK
127.0.0.1:6379> get k1
"v1"
当事物出现编译型异常的时候(代码有问题,命令有错),所有的命令都不会执行
如果是运行时候出现语法错误,错误命令抛出异常,但是其他命令正常执行,这就是没有原子性的表现!
#语法审查不过关
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> qwe
(error) ERR unknown command `qwe`, with args beginning with:
127.0.0.1:6379> get k1
QUEUED
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.
#出现逻辑错误的时候,抛出异常
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 "v1"
QUEUED
127.0.0.1:6379> incr k1
QUEUED
127.0.0.1:6379> get k1
QUEUED
127.0.0.1:6379> exec
1) OK
2) (error) ERR value is not an integer or out of range
3) "v1"
2、redis实现乐观锁、悲观锁
2.1、先看看没有锁带来的并发问题:
线程1
127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 10
QUEUED
127.0.0.1:6379> incrby out 10
QUEUED
线程2
127.0.0.1:6379> decrby money 10
(integer) 90
线程1提交之后
127.0.0.1:6379> exec
1) (integer) 80 #被其他线程影响
2) (integer) 10
出现了脏数据,其实mysql这样的关系型数据库通过隔离级别来解决,而redis没有隔离级别一说,直接通过底层的锁来实现:
2.1、乐观锁
127.0.0.1:6379> watch money #通过watch实现乐观锁
OK
加上乐观锁,上面的线程1提交之后就会
127.0.0.1:6379> exec
(nil) #提交失败
提交失败之后怎么办呢?
127.0.0.1:6379> unwatch money #放弃监视
OK
127.0.0.1:6379> watch money #重新监视
OK
(然后再次执行操作)
发现没有,其实就是自旋,不对不一样,重新获取最新值(放弃监视然后再监视)
【小结】
一般秒杀这样的场景会用到redis里面的乐观锁