Redis实战 - 5事务:multi、exec和watch
介绍
- redis的目标的是: 简洁,高效,由于事务本身就是一个很复杂的东西,所有我们不能把事务做的太复杂。
DISCARD
取消事务,放弃执行事务块内的所有命令。EXEC
执行所有事务块内的命令。MULTI
标记一个事务块的开始。UNWATCH
取消 WATCH 命令对所有 key 的监视。WATCH
key [key ...] 监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。MULTI 和 EXEC
127.0.0.1:6379> multi
OK
127.0.0.1:6379> lpush fruits orange
QUEUED
127.0.0.1:6379> lpush fruits nut
QUEUED
127.0.0.1:6379> lpush fruits apple
QUEUED
127.0.0.1:6379> exec
1) (integer) 1
2) (integer) 2
3) (integer) 3
我们发现命令是一起执行的,如果说我的某一条命令执行失败,会回滚吗?
答案是不会回滚
。看看下面的原子性。
原子性
事务的原子性是指要么事务全部成功,要么全部失败,那么 Redis 事务执行是原子性的么? 下面我们来看一个特别的例子。
> multi
OK
> set books iamastring # 执行成功
QUEUED
> incr books # 执行失败
QUEUED
> set poorman iamdesperate # 执行成功
QUEUED
> exec
1) OK
2) (error) ERR value is not an integer or out of range
3) OK
> get books
"iamastring"
> get poorman
"iamdesperate
上面的例子是事务执行到中间遇到失败了,因为我们不能对一个字符串进行数学运算,事务在遇到指令执行失败后,后面的指令还继续执行,所以 poorman 的值能继续得到设置。
到这里,你应该明白 Redis 的事务根本不能算「原子性」
,而仅仅是满足了事务的「隔离性」,隔离性中的串行化——当前执行的事务有着不被其它事务打断的权利。
Watch
我在执行lpush的时候,lpush被其他人改变了。
需求:在写multi的时候,不可以有其他的命令更改 “队列”中的集合。
出现并发问题,因为有多个客户端会并发进行操作。我们可以通过 Redis 的分布式锁来避免冲突,这是一个很好的解决方案。分布式锁是一种悲观锁,那是不是可以使用乐观锁的方式来解决冲突呢?
Redis 提供了这种 watch 的机制,它就是一种乐观锁。有了 watch 我们又多了一种可以用来解决并发修改的方法。 watch 的使用方式如下:
127.0.0.1:6379> watch msg
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set msg "hello wolrd"
QUEUED
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379> get msg
"12345"
127.0.0.1:6379>
注意事项
Redis 禁止在 multi 和 exec 之间执行 watch 指令,而必须在 multi 之前做好盯住关键变量,否则会出错。
.net操练,在StackExchange.Redis又该怎么做?
更复杂的事实是StackExchange.Redis使用的是多路复用器的方式。
我们不能只让并发调用方发布 WATCH / UNWATCH / MULTI / EXEC / DISCARD:这应该是混合在一起的。所以一个额外的抽象被给出:另外会让使事情更简单准确:约束。约束是预定义测试包括 WATCH 某种类型的测试并对结果进行检查。如果所有的约束都通过了,那么要么是以 MULTI / EXEC 发布(从事务开始,到执行整个事务块);要么是以 UNWATCH 发布(取消 WATCH 命令对所有 key 的监视)。阻止命令于其它调用方被混合在一起;所以例子可以是:
注意:从 CreateTransaction 返回的对象最后都是调用异步方法来执行命令(Execute方法最终也是调用ExecuteAsync,具体可以看源码):由于不知道每个操作的结果,除非在 Execute 或 ExecuteAsync 操作完成后。如果操作没有被执行,所有的Task 将被标记为取消,否则在命令执行后你可以获取每个正常的结果。
static void Transaction()
{
using (ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost:6379"))
{
IDatabase db = redis.GetDatabase();
ITransaction tran = db.CreateTransaction();
//为该事务添加先决条件
//强制指定指定的哈希字段不能存在。
tran.AddCondition(Condition.HashNotExists("transactionDemo", "UniqueID"));
tran.HashSetAsync("transactionDemo", "UniqueID","Unnn");
bool committed = tran.Execute();
Console.WriteLine(committed); //第1次执行,true,因为不存在,后面执行false,因为存在
}
}
通过 When 的内置操作
还应该注意的是,Redis已经为我们预料到了许多常见的场景(特别是:key/hash的存在,就像上面一样),还有单操作(single-operation)原子命令的存在。 通过 When 来访问,所以前面的示例也可以这样来实现:
var newId = CreateNewId();
bool wasSet = db.HashSet(custKey, "UniqueID", newId, When.NotExists);
注意:When.NotExists 会使用命令 HSETNX 而不会使用 HSET
HSETNX:如果字段已经存在于哈希表中,操作无效。
Redis实战 - 5事务:multi、exec和watch的更多相关文章
- redis实战之事务与持久化
1. 事务描述 (1)什么是事务 事务,就是把一堆事情绑在一起,按顺序的执行,都成功了才算完成,否则恢复之前的样子 事务必须服从ACID原则,ACID原则分别是原子性(atomicity).一致性(c ...
- 二十四 Redis消息订阅&事务&持久化
Redis数据类型: Redis控制5种数据类型:String,list,hash,set,sorted-set 添加数据,删除数据,获取数据,查看有多少个元素,判断元素是否存在 key通用操作 JR ...
- .Net Redis实战——事务和数据持久化
Redis事务 Redis事务可以让一个客户端在不被其他客户端打断的情况下执行多个命令,和关系数据库那种可以在执行的过程中进行回滚(rollback)的事务不同,在Redis里面,被MULTI命令和E ...
- redis multi exec
multi(),返回一个redis对象,并进入multi-mode模式,一旦进入multi-mode模式,以后调用的所有方法都会返回相同的对象,直到exec()方法被调用. phpredis是php的 ...
- Redis 实战 —— 07. 复制、处理故障、事务及性能优化
复制简介 P61 关系型数据库通常会使用一个主服务器 (master) 向多个从服务器 (slave) 发送更新,并使用从服务器来处理所有读请求. Redis 也采用了同样的方法实现自己的复制特性,并 ...
- Redis实战:如何构建类微博的亿级社交平台
微博及 Twitter 这两大社交平台都重度依赖 Redis 来承载海量用户访问.本文介绍如何使用 Redis 来设计一个社交系统,以及如何扩展 Redis 让其能够承载上亿用户的访问规模. 虽然单台 ...
- Redis实战篇
Redis实战篇 1 Redis 客户端 1.1 客户端通信 原理 客户端和服务器通过 TCP 连接来进行数据交互, 服务器默认的端口号为 6379 . 客户端和服务器发送的命令或数据一律以 \r\n ...
- Redis 中的事务分析,Redis 中的事务可以满足ACID属性吗?
Redis 中的事务 什么是事务 1.原子性(Atomicity) 2.一致性(Consistency) 3.隔离性(Isolation) 4.持久性(Durability) 分析下 Redis 中的 ...
- Redis系列之key操作命令与Redis中的事务详解(六)
序言 本篇主要目的有二: 1.展示所有数据类型中key的所有操作命令,以供大家学习,查阅,更深入的挖掘redis潜力. 2.掌握redis中的事务,让你的数据完整性一致性拥有更优的保障. redis命 ...
随机推荐
- js中字符串可以调用的方法
var s = "hello,world" //定义一个字符串 s.length() // => 11 s.charAt(0) ...
- Linux lvs三种模式工作原理
LVS:Linux Virtual Sevser,Linux虚拟服务器,由章文嵩先生成立的有关负载均衡的开源项目.基于Linux内核.优化后并发量可以达到百万级. NAT模式: NAT(Network ...
- git学习01- 下载安装&初始化库&提交
1.windows下安装git,git官网下载安装包安装 2.本地创建一个目录,在目录下创建1个文本 readme.txt 3.cmd进入到该目录,执行git init,初始化git仓库 4.添加文件 ...
- CF1157B-Long Number题解
原题地址 题目大意:有一个\(n\)位数,其中的数字只有\(1\)~\(9\),不包括\(0\),每个\(1\)~\(9\)的数字有一个映射,映射也在\(1\)~\(9\)中,现在我们可以对这个\(n ...
- Git如何合并一个已经在GitHub上提交但没有合并的Pull Request请求
步骤 进入Git仓库,执行curl -L https://github.com/<USER>/<REPO>/pull/<NO>.patch | git am
- [PKUWC2019]Day1 T2 你和虚树的故事
选择k个颜色,使得颜色的虚树有交的方案数 肯定要考虑连通块的贡献. 法一 https://www.cnblogs.com/xzz_233/p/10292983.html 枚举连通块还是不可行的. 枚举 ...
- requests爆破+简单数字验证码
验证码用正则获取 然后提交:访问方式为get:由于有验证码 要用session:注意:获取验证码的时候所打开的网页要用当前session(即要get的) 不能另外打开(如urllib中的打开网页)网页 ...
- [APIO2017]商旅(floyd+分数规划+SPFA)
题解:首先肯定要跑最短路,而n<=100,所以可以用floyd,然后根据比值,很容易想到二分答案,然后再SPFA跑一遍负环,就能求出解了. #include<bits/stdc++.h&g ...
- 在vue中关于element UI 中表格实现下载功能,表头添加按钮,和点击事件失效的解决办法。
因为在element 中表格是使用el-table的形式通过数据来支撑结构,所以,表格的样式没有自己写的灵活,所以有了没法添加按钮的烦恼.下面是解决的方法. 准备工作: 一.下载npm安装包两个 1. ...
- 3-ftp搭建成功,服务器能访问,外网无法连接和访问
登录 ECS 管理控制台,找到相应的实例. 在实例的右侧单击管理,进入实例详情页面.选择本实例安全组. 在安全组列表页面,找到相应的安全组,单击配置规则. 在安全组规则页面,单击添加安全组规则. 在添 ...