Redis基础—5、Redis事务、乐观锁
一、redis事务简介
1、简介
Redis 事务可以一次执行多个命令, 并且带有以下三个重要的保证:
- 批量操作在发送 EXEC 命令前被放入队列缓存。
- 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。
- 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。
一个事务从开始到执行会经历以下三个阶段:
- 开始事务。
- 命令入队。
- 执行事务。
我们可以理解Redis事物为一个脚本,脚本中如果出现编译时错误(代码有错),整条脚本都不会被执行,并且并抛弃。脚本中如果出现运行时错误(1/0等逻辑错误),除了该条指令其他的语句依然会被执行。
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> set k3 v3
QUEUED
127.0.0.1:6379> exec #执行事务
1) OK
2) OK
3) OK
127.0.0.1:6379> get k1 #可以拿到
"v1"
127.0.0.1:6379> get k3
"v3"
127.0.0.1:6379> multi #开启事务
OK
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> discard #放弃事务
OK
127.0.0.1:6379> get k4 #拿不到k4
(nil)
127.0.0.1:6379> multi #开启事务
OK
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> se k5 v5
(error) ERR unknown command `se`, with args beginning with: `k5`, `v5`, #语法错误
127.0.0.1:6379> set k6 v6
QUEUED
127.0.0.1:6379> exec #执行
(error) EXECABORT Transaction discarded because of previous errors. #所有事务都不会执行
127.0.0.1:6379> get k6 #拿不到
(nil)
127.0.0.1:6379> multi #开启事务
OK
127.0.0.1:6379> set k7 v7
QUEUED
127.0.0.1:6379> incr k7 #对字符串进行增加一操作(逻辑错误)
QUEUED
127.0.0.1:6379> set k8 v8
QUEUED
127.0.0.1:6379> exec #其他的会被执行
1) OK
2) (error) ERR value is not an integer or out of range
3) OK
127.0.0.1:6379> get k8
"v8"
二、Redis实现乐观锁
悲观锁:
- 很悲观,认为什么时候都会出问题,无论做什么都会加锁
乐观锁:
- 很乐观,认为什么时候都不会出现问题,所以不会加锁!更新数据的时候去判断一下,在此期间是否有人修改过这个数据
- 获取version
- 更新的时候比较version
我们在redis中可以通过watch来实现对一个对象的乐观锁,通过watch来获取一个属性的version。
下面是具体事例:
127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money
(error) ERR wrong number of arguments for 'decrby' command
127.0.0.1:6379> discard
OK
127.0.0.1:6379> watch money #监控money的值
OK
127.0.0.1:6379> multi #开启事务
OK
127.0.0.1:6379> decrby money 30 #减少余额30
QUEUED
127.0.0.1:6379> incrby out 20 #增加支出20
QUEUED
127.0.0.1:6379> exec #执行
1) (integer) 70
2) (integer) 20
127.0.0.1:6379> watch money #先查看下money
OK
127.0.0.1:6379> multi #开启一下
OK
127.0.0.1:6379> incrby money 50
QUEUED
这时我们不提交事务,我们模拟一下另外一个线程对money进行改变的场景,这里第二个线程将这个钱数减少了20
127.0.0.1:6379> get money
"70"
127.0.0.1:6379> decrby money 50
(integer) 20
再执行下第一个线程的事务
127.0.0.1:6379> get money
QUEUED
127.0.0.1:6379> exec
(nil)
这里执行出现nil,这就代表着乐观锁生效了,事务没有执行成功。
那假如我们在第二个线程也对money做了一些改变,但是最终值与原来第一个线程watch时的值相同呢?
第一个线程,
127.0.0.1:6379> set jj 80
OK
127.0.0.1:6379> watch jj #加锁
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incrby jj 30 #这里第二个线程先对他增加50,再减少50
QUEUED
127.0.0.1:6379> exec #执行
(nil)
发现不大行,redis的事务处理与其值没有很大的关系。