Redis事务
Redis 通过 MULTI 、EXEC、 DISCARD 和 WATCH 四个命令来实现事务功能。
MULTI :标记一个事务块的开始。
EXEC: 执行所有事务块内的命令。
DISCARD :取消事务,放弃执行事务块内的所有命令。
WATCH key [key ...] :监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。
一、事务
1、是什么?
可以一次执行多个命令,本质是一组命令的集合。一个事务中的所有命令都会序列化,不许加塞!
2、能干什么?
一个队列中,一次性的、顺序性的、排他性的执行一系列的命令。要么一起成功,要么一起失败。排好队,一次性的执行多个redis的命令!
3、怎么玩?
通过MULTI指令开启,之后输入多个命令!Redis将它们加入到队列当中,所有的命令通过EXEC来开启执行!通过DISCARD来放弃本次的批处理操作!
(1)正常执行(好比购物先加入购物车,最后EXEC统一结账。)
192.168.1.66:6379> MULTI --开启事务 OK 192.168.1.66:6379> set k1 v1 QUEUED 192.168.1.66:6379> set k2 v2 QUEUED 192.168.1.66:6379> set k3 v3 QUEUED 192.168.1.66:6379> set k4 v4 QUEUED 192.168.1.66:6379> get k2 QUEUED 192.168.1.66:6379> EXEC --结束事务 1) OK 2) OK 3) OK 4) OK 5) "v2"
(2)DISCARD 撤销所有操作!
192.168.1.66:6379> MULTI OK 192.168.1.66:6379> set k1 11 QUEUED 192.168.1.66:6379> set k2 22 QUEUED 192.168.1.66:6379> set k3 33 QUEUED 192.168.1.66:6379> DISCARD OK 192.168.1.66:6379> get k1 --这里的k1的value值还是上面的值,而不是新设置的value值 "v1"
(3)加入队列时不出错,下面的都将正常运行,执行时出错不影响其他语句!
192.168.1.66:6379> set k1 v1 OK 192.168.1.66:6379> set k2 v2 OK 192.168.1.66:6379> MULTI OK 192.168.1.66:6379> incr k1 QUEUED 192.168.1.66:6379> set k2 22 QUEUED 192.168.1.66:6379> EXEC 1) (error) ERR value is not an integer or out of range --在运行是才发现k1不是数字,报错 2) OK --执行成功
注意:
(1)当遇到执行错误时,redis放过这种错误,保证事务执行完成。(所以说redis的事务并不是保证原子性)
(2)与mysql中事务不同,在redis事务遇到执行错误的时候,不会进行回滚,而是简单的放过了,并保证其他的命令正常执行。这个区别在实现业务的时候,需要自己保证逻辑符合预期。
(3)当事务的执行过程中,如果redis意外的挂了。很遗憾只有部分命令执行了,后面的也就被丢弃了。当然如果我们使用的append-only file方式持久化,redis会用单个write操作写入整个事务内容。即是是这种方式还是有可能只部分写入了事务到磁盘。发生部分写入事务的情况 下,redis重启时会检测到这种情况,然后失败退出。可以使用redis-check-aof工具进行修复,修复会删除部分写入的事务内容。修复完后就 能够重新启动了。
二、WATCH
WATCH 命令可以为 Redis 事务提供 check-and-set (CAS)行为。
被 WATCH 的键会被监视,并会发觉这些键是否被改动过了。 如果有至少一个被监视的键在 EXEC 执行之前被修改了, 那么整个事务都会被取消, EXEC 返回空多条批量回复(null multi-bulk reply)来表示事务已经失败。
1、首先我们不加WATCH进行事务处理
时间 |
客户端 A |
客户端 B |
说明 |
T1 |
SET name aa SET age 10 |
GET name aa GET age 10 |
数据库中两客户端登录,及键初始值。 它们是同步的。 |
T2 |
MULTI SET name bb Incr age |
此时,客户端1开启事务,并提交队列命令: 1.想要将当前age自增+1运算; 2.将name值改为bb |
|
T3 |
SET name cc Incr age |
此时,客户端2修改了age值 同时修改name值为cc |
|
T4 |
EXEC |
|
|
T5 |
GET name bb GET age 12 |
此时,客户端1执行队列命令,发现运算之后age不是理想中的11,而是12原因是被其它客户插足抢先给修改了。name值确实是没有被其它所改变。这样可能导致数据不一致性。 |
2、添加WARCH
客户端1-引入“乐观锁”机制 | 客户端2 | 说明 |
SET age "10" SET name "zhangsan" |
GET age "10" GET name "zhangsan" |
数据库中两客户端登录,及键初始值。 |
WATCH age name OK MUTLI OK incr age QUEUED set name lisi QUEUED |
此时,客户端1用watch命令监视age和name,然后开启事务,并提交队列命令 | |
incr age (integer) 11 |
此时,客户端2修改了age值 | |
exec (nil) get age "11" get name "zhangsan" |
此时,客户端1执行队列命令,由watch监控发现此期间age的值已经被修改过,则让事整个务回滚,不做任何动作。
watch可以同时监控多个键,在监控期间只要有一个键被其它客户端改变,则整个事务回滚。 |
想太多,做太少,中间的落差就是烦恼。想没有烦恼,要么别想,要么多做。少校【1】