StackExchange.Redis学习笔记(四) 事务控制和Batch批量操作
Redis事物
Redis命令实现事务
Redis的事物包含在multi和exec(执行)或者discard(回滚)命令中
和sql事务不同的是,Redis调用Exec只是将所有的命令变成一个单元一起执行,期间不会插入其他的命令。
这种方式不保证事务的一致性,即使中间有一条命令出错了,其他命令仍然可以正常执行,并且无法回滚
下面的例子演示了一个基本的事务操作
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set name mike
QUEUED
127.0.0.1:6379> set age 20
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
127.0.0.1:6379> get name
"mike"
可以看到,直到调用Exec命令时,才开始执行之前的所有命令,同时会返回两个结果,discard 命令类似,就不贴代码了。
下面模拟一个会报错的命令来看一下
127.0.0.1:6379> get age
"20"
127.0.0.1:6379> get name
"mike"
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr name
QUEUED
127.0.0.1:6379> incr age
QUEUED
127.0.0.1:6379> exec
1) (error) ERR value is not an integer or out of range
2) (integer) 21
127.0.0.1:6379> get age
"21"
127.0.0.1:6379>
我们同时将用户name和age 进行自增1操作,然而name不是数字类型,结果执行失败,但是age的自增操作仍然成功了。这无疑是个很令人不舒服的弊端,所以在写相关代码时要注意
乐观锁
前面说到通过multi命令只是保证一个事物中的所有命令可以在一起执行,显然只是实现这一点的话对于大部分的业务都是无法满足的。
所以Redis提供了Watch命令来监控一个key以达到乐观锁的效果。关于乐观锁的原理有不了解的小伙伴可以抽十分钟去科普一下
下面展示一个乐观锁实例:

这里模拟了两个客户端同时操作一个相同的键
左边为client1,我们用watch监控了name和age两个键,然后分别设置name和age的值。在exec命令之前,通过另一个客户端client2设置了name的值。
client1执行exec命令时,Redis检测到name的值已经被其他客户端改过了,因此在事物中的所有命令都会回滚。
watch命令是对整个连接有效的,用完之后可以用discard、unwatch、exec命令清除监视
StackExchange.Redis中的事物控制
在StackExchange.Redis是无法用watch multi命令来执行的,因为在并发环境下,会产生多个watch multi命令,全混在一起就乱套了。
但是StackExchange.Redis提供了一套非常简单易懂的创建事物的方式 ,下面为示例代码
public void TestTran()
{
IDatabase db = StackExchangeRedisHelper.GetDatabase();
string name = db.StringGet("name");
string age = db.StringGet("age");
Console.WriteLine("NAME:" + name);
Console.WriteLine("Age:" + age);
var tran = db.CreateTransaction();
tran.AddCondition(Condition.StringEqual("name", name));
Console.WriteLine("tran begin");
tran.StringSetAsync("name", "leap");
tran.StringSetAsync("age", );
Thread.Sleep();
bool result = tran.Execute();
Console.WriteLine("执行结果:" + result);
Console.WriteLine("Age:" + db.StringGet("age"));
Console.WriteLine("Name:" + db.StringGet("name"));
}
这里通过CreateTransaction函数(multi)来创建一个事物,调用其Execute函数(exec)提交事物,其中的 "Condition.StringEqual("name", name)" 就相当于Redis命令中的watch name。
其中睡眠四秒是我需要在事物提交之前打开另一个客户端来修改name的值.最终的执行结果如下
NAME:leo
Age:20
tran begin
执行结果:False
Age:20
Name:mike
在程序睡眠期间我用另一个客户端将name改成了mike,所以事物最终执行失败
通过查询Redis的慢日志。其调用的命令也是watch multi exec。(慢日志没有记录Exec命令,实际上是执行了的)。
我们可以通过设置redis.windows-service.conf文件中的slowlog-log-slower-than的值为0让Redis记录所有的命令日志
127.0.0.1:6379> slowlog get 100
1) 1) (integer) 293
2) (integer) 1511257634
3) (integer) 1
4) 1) "GET"
2) "name"
2) 1) (integer) 292
2) (integer) 1511257634
3) (integer) 0
4) 1) "GET"
2) "age"
3) 1) (integer) 291
2) (integer) 1511257634
3) (integer) 3
4) 1) "SELECT"
2) "0"
4) 1) (integer) 290
2) (integer) 1511257634
3) (integer) 1
4) 1) "SET"
2) "age"
3) "12"
5) 1) (integer) 289
2) (integer) 1511257634
3) (integer) 1
4) 1) "SET"
2) "name"
3) "leap"
6) 1) (integer) 288
2) (integer) 1511257634
3) (integer) 1
4) 1) "MULTI"
7) 1) (integer) 287
2) (integer) 1511257634
3) (integer) 3
4) 1) "GET"
2) "name"
8) 1) (integer) 286
2) (integer) 1511257634
3) (integer) 11
4) 1) "WATCH"
2) "name"
9) 1) (integer) 285
2) (integer) 1511257634
3) (integer) 4
4) 1) "GET"
2) "age"
10) 1) (integer) 284
2) (integer) 1511257634
3) (integer) 6
4) 1) "GET"
2) "name"
这里可能大家会有个疑惑,既然tran是直接调用的watch multi等命令,为什么不会有并发的顺序问题?
这是因为Tran开启后,所做的watch,stringset等操作,都会再调用Exec函数时把相应的命令封装成一个请求发送给Redis一起执行。这样每个事务之间都是独立的,就不会有问题了。
Batch批量操作
StackExchange.Redis中对于连续多次的缓存等请求,我们会多次调用相关的函数来执行Redis命令。然而这种方式有个弊端就是每一次的请求都需要等待返回结果
如果在网络状况不好的情况下,可能会造成不好的用户体验。
对于这种问题可以用StackExchange.Redis提供的CreateBatch()解决
public void TestPipeLine()
{
IDatabase db = StackExchangeRedisHelper.GetDatabase();
var batch = db.CreateBatch();
Task t1 = batch.StringSetAsync("name", "bob");
Task t2 = batch.StringSetAsync("age", );
batch.Execute();
Task.WaitAll(t1, t2);
Console.WriteLine("Age:" + db.StringGet("age"));
Console.WriteLine("Name:" + db.StringGet("name"));
}
batch会把所需要执行的命令打包成一条请求发到Redis,然后一起等待返回结果。这样批量操作的速度就大大提升啦!
StackExchange.Redis学习笔记(四) 事务控制和Batch批量操作的更多相关文章
- StackExchange.Redis学习笔记(五) 发布和订阅
Redis命令中的Pub/Sub Redis在 2.0之后的版本中 实现了 事件推送的 发布订阅命令 以下是Redis关于发布和订阅提供的相关命令 SUBSCRIBE channel [channe ...
- StackExchange.Redis学习笔记(二) Redis查询 五种数据类型的应用
ConnectionMultiplexer ConnectionMultiplexer 是StackExchange.Redis的核心对象,用这个类的实例来进行Redis的一系列操作,对于一个整个应用 ...
- StackExchange.Redis学习笔记(三)
这一章主要写一些StackExchange.Redis的配置及不太经常用到的函数 数据库连接 下面是我的连接字符串,里面指定了地址,密码,及默认的数据库 Redis启动后默认会分成0-15个数据库,不 ...
- StackExchange.Redis学习笔记(三) 数据库及密码配置 GetServer函数
这一章主要写一些StackExchange.Redis的配置及不太经常用到的函数 数据库连接 下面是我的连接字符串,里面指定了地址,密码,及默认的数据库 Redis启动后默认会分成0-15个数据库,不 ...
- StackExchange.Redis学习笔记(一) Redis的使用初探
Redis Redis将其数据库完全保存在内存中,仅使用磁盘进行持久化. 与其它键值数据存储相比,Redis有一组相对丰富的数据类型. Redis可以将数据复制到任意数量的从机中 Redis的安装 官 ...
- Redis学习笔记(7)-事务
package cn.com; import java.util.List; import redis.clients.jedis.Jedis; import redis.clients.jedis. ...
- Redis学习笔记四:独立功能之发布与订阅
客户端可以通过执行 subscribe 命令订阅一个或多个频道,每当有其他客户端向被订阅的频道发送消息时,频道所有的订阅者都会收到这条消息. 客户端还可以通过执行 psubscribe 命令订阅一个或 ...
- Redis 学习笔记四 Mysql 与Redis的同步实践
一.测试环境在Ubuntu kylin 14.04 64bit 已经安装Mysql.Redis.php.lib_mysqludf_json.so.Gearman. 点击这里查看测试数据库及表参考 本文 ...
- Redis学习笔记~目录
回到占占推荐博客索引 百度百科 redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合). ...
随机推荐
- java连接sqlserver2008r2 心得
现在是该轻松一笑的时候了,困扰已久的问题有了解释了. 之前的各种连不上,说到底还是权限问题,sqlserver2008r2的权限分得太细了. 两个实例间数据库互相都看不到,更不用谈访问了. 端口号也是 ...
- 四种Sandcastle方法生成c#.net帮助类帮助文档
方法一 前端时间在网上收集和自己平时工作总结整理了<干货,比较全面的c#.net公共帮助类>,整理完成上传github之后我又想,既然是帮助类,总得有个帮助文档于是乎想到了Sandcast ...
- 安装Sublime Text 3插件的方法:
安装Sublime Text 3插件的方法: 朋友们,小站活着不容易,全靠广告费养着了,如果本文对你有帮助.麻烦动下手点下页面的广告吧,谢谢! 直接安装 安装Sublime text 2插件很方便,可 ...
- Java中public,protected,default,private的访问权限问题(简明扼要)
import packa.*;//导入了packa包中所有的类.(不包括包中的子包)一般不会用,用哪个导入哪个. 导包的原则:用到哪个类,就导入哪个类.所有字母都小写. 权限列表: public ...
- 【网络爬虫入门04】彻底掌握BeautifulSoup的CSS选择器
[网络爬虫入门04]彻底掌握BeautifulSoup的CSS选择器 广东职业技术学院 欧浩源 2017-10-21 1.引言 目前,除了官方文档之外,市面上及网络详细介绍BeautifulSoup ...
- Ubuntu14.04LTS下安装Node.js&NPM以及个人博客hexo的初始化配置
什么是hexo Hexo 是一款基于node 的静态博客网站生成器作者 :tommy351是一个台湾的在校大学生...相比其他的静态网页生成器而言有着,生成静态网页最快,插件丰富(已经移植了大量Oct ...
- Java 递归调用 recursive 给一个参数 返回一大堆
需求: 需要组装成对象多层嵌套式的 json字符串; 想到使用 递归来完成这个多层嵌套: 憋了四个小时,终于写出来了; 先看效果: 数据库中的数据: 拼装后的效果: [ EmpVO{ ename='孙 ...
- Spark: Job in detail
1.sc.runJob -> dagScheduler.runJob -> submitJob 2.DAGSchedueler::submitJob 会创建 JobSubmitted 的 ...
- Kinect v2(Microsoft Kinect for Windows v2 )配置移动电源解决方案
Kinect v2配置移动电源解决方案 Kinect v2如果用于移动机器人上(也可以是其他应用场景),为方便有效地展开后续工作,为其配置移动电源是十分必要的. 一.选择移动电源 Kinect v2原 ...
- Fastify 系列教程二 (中间件、钩子函数和装饰器)
Fastify 系列教程: Fastify 系列教程一 (路由和日志) Fastify 系列教程二 (中间件.钩子函数和装饰器) 中间件 Fastify 提供了与 Express 和 Restify ...