redis 分布式缓存实战-redis 事务
1.描述
redis 事务单独的隔离操作:事务中的所有命令都会序列化、按顺序执行。事务在执行过程中,不会被其他客户端发送过来的命令请求所打断。
redis 事务没有隔离级别的概念:队列中的命令没有提交之前都不会实际的被执行,因为事务提交前任何指令都不会被实际执行,也就不存在“事务内的查询看到事务里的更新,在事务外查询不能看到”。
redis 事务不保证原子性:redis 同一个事务中如果有一条命令运行时执行失败,其后的命令仍然会被执行,没有回滚。
2.命令
Multi、Exec、Discard和Watch是Redis事务的相关命令。
Multi:标记一个事务块的开始。Multi 命令用于开启一个事务,它总是返回 OK 。 Multi 执行之后, 客户端可以继续向服务器发送任意多条命令, 这些命令不会立即被执行, 而是被放到一个队列中, 当 Exec命令被调用时, 所有队列中的命令才会被执行。另一方面, 通过调用 Discard, 客户端可以清空事务队列, 并放弃执行事务。
Exec:执行所有事务块内的命令。命令的回复是一个数组, 数组中的每个元素都是执行事务中的命令所产生的回复。 其中, 回复元素的先后顺序和命令发送的先后顺序一致。当客户端处于事务状态时, 所有传入的命令都会返回一个内容为 Queued 的状态回复(status reply), 这些被入队的命令将在 Exec 命令被调用时执行。
Discard:取消事务,放弃执行事务块内的所有命令。当执行Discard命令时,事务会被放弃,事务队列会被清空,并且客户端会从事务状态中退出。
Watch:监视一个或多少Key,如果在事务执行之前这个或这些Key被其他命令所改动,那么事务将被打断。
UnWatch:取消Watch命令对所有Key的监视。
3.示例
3.1. 正常执行示例
首先我们清空数据库内容,查看内容能看到数据库为空。然后Multi 开启一事务,设置两个 Key、Value值,Exec 执行事务。我们看到执行事务时,同时返回两命令的执行结果。通过查询数据库正常保存两数据内容。
3.2. 放弃事务示例
接下来,我们再来试下,放弃事务示例,首先我们清空数据库内容,查看内容能看到数据库为空。然后Multi 开启一事务,设置两个 Key、Value值,此时,我们执行Discard命令,放弃事务。通过查看数据库中内空,我们可以看到数据库中还是为空。
3.3. 全体连坐示例
全体连坐指的是什么呢?就是说其中有一条命令编译时错误,整个系列命令都将不会被执行。
3.4. 冤头债主示例
冤头债主指的是什么呢?就是说其中有一条命令运行时有问题,系列中没问题的命令会执行,有问题的命令不会成功执行。
3.5. Watch监控
Watch 命令可以为 Redis 事务提供 check-and-set (CAS)行为。被 Watch 的键会被监视,并会发觉这些键是否被改动过了。 如果有至少一个被监视的键在 Exec 执行之前被修改了, 那么整个事务都会被取消, Exec 返回nil-reply来表示事务已经失败。
下面我们以信用卡账号结余和债务为例:
先初始账号结余为100,债务为0。消费20,账号结余减20,债务增加20。首先Watch监视账号结余,然后开启事务,对账号结余及债务进行操作。
当无加塞篡改时,正常执行结果,账号结余为80,债务20。
当Watch监视账号结余,有加塞篡改账号结余,比如向账号充值100,账号结余改为180时,再执行一系列命令,执行事务时,得到的结果将是未能正常更新操作。
4.总结
我们发现 Redis 对于事务,部分支持,不能像SQL Server等关系数据库的强一致性。Watch指令,类似乐观锁,事务提交时,如果Key的值已被别的客户端改变,比如某个List已被别的客户端Push/Pop过了,整个事务队列都不会被执行。通过Watch命令在事务执行之前监控多个Keys,倘若在Watch之后有任何Key的值发生了改变,Exec命令执行的事务都将被放弃,同时返回Nullmulti-bulk应答以通知调用者事务执行失败。
至此Redis事务介绍完毕,有不当地方,欢迎指正!