redis事务及锁的应用
一、redis支持简单的事务(不支持回滚)
redis提供的事务是将多个命令打包,然后一次性、按照先进先出的顺序(FIFO)有序的执行。在执行过程中不会被打断(在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中),当事务队列中的所以命令都被执行(无论成功还是失败)完毕之后,事务才会结束。
MULTI //开始事务
SET ... //命令1入队
GET ... //命令1入队
SADD ... //命令1入队
......
EXEC //执行事务(一次执行1.2.3...)
如上所述,Redis的事务以特殊命令MULTI开始,之后跟着用户传入的多个命令,最后以EXEC未结束。由于这种简单的事务在EXEC命令被调用之前不会执行任何实际操作,所以用户将没有办法根据根据读到的数据来做决定。
二、redis与mysql的区别
| Mysql | Redis |
开启 | Start transtration | multi |
语句 | 普通sql | 普通命令 |
失败 | Rollback回滚 | Discard取消 |
成功 | Commit | Exec |
注:
rollback与discard的区别
如果已经成功执行了两条语句,第三条语句出错
Rollback后前两条语句影响消失
Discard只是结束本次事务,前两条语句照成的影响仍然还在
注:
在mulit后面的语句中,语句出错肯能有两种情况
1)语法就有问题
这种exec时报错,所有语句得不到执行
2)语法本身没错,但适用对象有问题,比如zadd操作link对象,exec之后回执行正确的语句,并跳过不适当的语句
(如果zadd操作link这种事怎么避免?这一点由程序员解决)
Eg:
正常执行
127.0.0.1:6379> set wang 200
OK
127.0.0.1:6379> set zhao 700
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby zhao 100
QUEUED
127.0.0.1:6379> incrby wang 100
QUEUED
127.0.0.1:6379> get zhao
QUEUED
127.0.0.1:6379> exec
1) (integer) 600
2) (integer) 300
3) "600"
错误执行1:语法错误
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby zhao 100
QUEUED
127.0.0.1:6379> sda
(error) ERR unknown command `sda`, with args beginning with:
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> mget zhao wang
1) "600"
2) "300"
错误执行2:执行对象不对
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby zhao 100
QUEUED
127.0.0.1:6379> sadd wang pig
QUEUED
127.0.0.1:6379> exec
1) (integer) 500
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
127.0.0.1:6379> mget zhao wang
1) "500"
2) "300"
思考:
我正在买票
Ticket-1,money-100
而票只有一张,如果在multi之后和exec之前,票被别人买走了,即ticket变成0了,
我该如何观察这种情况,并不在提交
悲观想法
世界充满危险。肯定有人和我抢,只有我能操作[悲观锁]
乐观想法
没有那么多人和我抢,因此我只需要注意,有没有人更改了ticket的值就可以了[乐观的]
Redis的事务中,启用的是乐观锁,只负责监控key没有被改动
具体的指令----- watch命令
127.0.0.1:6379> set ticket 1
OK
127.0.0.1:6379> set lisi 300
OK
127.0.0.1:6379> set wang 300
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decr ticket
QUEUED
127.0.0.1:6379> decrby lisi 100
QUEUED
执行exec之前 另外一个线程操作了减票操作
127.0.0.1:6379> decr ticket
(integer) 0
这时再执行exec,就会出现票为-1
127.0.0.1:6379> exec
1) (integer) -1
2) (integer) 200
Watch命令
127.0.0.1:6379> set ticket 1
OK
127.0.0.1:6379> watch ticket
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decr ticket
QUEUED
127.0.0.1:6379> decrby lisi 100
QUEUED
执行exec之前 另外一个线程操作了减票操作
127.0.0.1:6379> decr ticket
(integer) 0
这时再执行exec,就会出现执行不成功
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379> mget ticket lisi
1) "0"
2) "200"
unwatch取消监视
127.0.0.1:6379> UNWATCH
OK