redis在.net架构中的应用(2)--并发和原子操作不可兼得
在上一篇文章中,我主要向大家介绍了利用servicestack连接redis以及一些redis的基本数据类型,传送门
本文中,我将通过一个具体应用场景为大家介绍redis中的并发和原子操作
其中用到的redis命令,请大家去redis官网查询http://www.redis.io/commands
一 一个投票统计的应用场景
假设我要做一个实时统计投票数的应用,这个投票总共有A、B、C、D四个选项,因为是一个高并发的场景,所以我准备用redis来存储投票数
我们首先利用redis-cli模拟这个过程,打开命令终端,新建一个hash类型的key,叫做TicketCount, 编号为1,然后我们将选项作为其field值,
redis命令如下:

然后我们利用servicestack模拟投票场景,代码如下:
static void Main(string[] args)
{ RedisClient[] redisCli = new RedisClient[]{
new RedisClient("192.168.101.165", ),
new RedisClient("192.168.101.165", ),
new RedisClient("192.168.101.165", ),
new RedisClient("192.168.101.165", ),
new RedisClient("192.168.101.165", ),
new RedisClient("192.168.101.165", ),
new RedisClient("192.168.101.165", ),
new RedisClient("192.168.101.165", )
};
ThreadPool.QueueUserWorkItem(o =>
{
testTicketCount(redisCli[], "A");
});
ThreadPool.QueueUserWorkItem(o =>
{
testTicketCount(redisCli[], "A");
});
ThreadPool.QueueUserWorkItem(o =>
{
testTicketCount(redisCli[], "B");
});
ThreadPool.QueueUserWorkItem(o =>
{
testTicketCount(redisCli[], "B");
});
ThreadPool.QueueUserWorkItem(o =>
{
testTicketCount(redisCli[], "C");
});
ThreadPool.QueueUserWorkItem(o =>
{
testTicketCount(redisCli[], "C");
});
ThreadPool.QueueUserWorkItem(o =>
{
testTicketCount(redisCli[], "D");
});
ThreadPool.QueueUserWorkItem(o =>
{
testTicketCount(redisCli[], "D");
}); Console.Read();
}
/// <summary>
/// 对某个选项进行投票,投票数加1
/// </summary>
/// <param name="rediscli"></param>
/// <param name="field"></param>
internal static void testTicketCount(IRedisClient rediscli, string field)
{
int k = ;
for (int i = ; i <= ; i++)
{ k = int.Parse(rediscli.GetValueFromHash("TicketCount", field))+;
rediscli.SetEntryInHash("TicketCount", field, k.ToString());
}
}
我们设想的结果是A、B、C、D四个选项各获得了200票,但是对多线程比较熟悉的同学马上就能看出问题了,
事实上最终的结果是

没错,在一个线程执行GetValueFromHash和SetEntryInHash两条命令的时候,另一个线程修改了key的值,破坏了操作的原子性

这个过程中,A选项明显丢掉了一张投票。
二 Nosql中的原子性
要保证数据操作的原子性,在传统的RDBMS应用中,我们首先想到的就是事物和锁,但是在这个场景中,我们需要获得保证{get key;set key}这两步
操作的原子性,我们需要获得key的值,所以无法用到事物。
让我们回到nosql的本质上来,来谈谈锁的运用,有兴趣的同学可以看看关于nosql的CAP原则和传统的RDBMS的ACID原则:


从上图中,我们可以看到,NoSQL系统更加注重性能,是不保证一客户端的两个操作的原子性保证的
(redis中的事物比较特殊,我将会在下一篇文章中讨论)
三 利用hincrby
幸亏redis还提供hincrby命令,也就是直接对hset某个字段的值加上某个整数
也就是对某个hash key中的某个value 值 提供加一操作。我们可以将TicketCount函数修改一下:
internal static void testTicketCount(IRedisClient rediscli, string field)
{
int k = ;
for (int i = ; i <= ; i++)
{
rediscli.IncrementValueInHash("TicketCount", field, );
//k = int.Parse(rediscli.GetValueFromHash("TicketCount", field))+1;
//rediscli.SetEntryInHash("TicketCount", field, k.ToString());
}
}
但是问题是,如果换了一个应用场景,A中存储的不是数值而是字符串呢?
四 并发和原子操作
这两个特性是完全矛盾的,nosql的设计理念就是为了支持高并发,所以它注定就对原子操作的支持性不高。
因为原子操作必然要涉及到数据库级别的锁,会带来各种性能损耗。
至于redis中的事物,完全是和redis的实现机制有关的,我将会在下一篇文章中和大家讨论
有同学提到了乐观锁机制,servicestack中也已经实现了乐观锁,我也会在下一篇中提到
作者:Mars
出处:http://www.cnblogs.com/marsblog/
本文基于署名-非商业性使用 3.0中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名 Mars (包含链接)
redis在.net架构中的应用(2)--并发和原子操作不可兼得的更多相关文章
- redis在.net架构中的应用(1)--使用servicestack连接redis(转)
引言:作为少有的.net架构下的大型网站,stackoverflow曾发表了一篇文章,介绍了其技术体系,原文链接http://highscalability.com/blog/2011/3/3/sta ...
- redis在.net架构中的应用(1)--使用servicestack连接redis
引言:作为少有的.net架构下的大型网站,stackoverflow曾发表了一篇文章,介绍了其技术体系,原文链接http://highscalability.com/blog/2011/3/3/sta ...
- redis在.net架构中的应用(1)--利用servicestack连接redis
引言:作为少有的.net架构下的大型网站,stackoverflow曾发表了一篇文章,介绍了其技术体系,原文链接http://highscalability.com/blog/2011/3/3/sta ...
- 微信小程序大型系统架构中应用Redis缓存要点
在大型分布式系统架构中,必须选择适合的缓存技术以应对高并发,实现系统相应的高性能,酷客多小程序经过慎重选型,选择了采用基于腾讯云服务的Redis弹性缓存技术,结合Redis官方推荐的.NET驱动类库S ...
- 39、生鲜电商平台-redis缓存在商品中的设计与架构
说明:Java开源生鲜电商平台-redis缓存在商品中的设计与架构. 1. 各种计数,商品维度计数和用户维度计数 说起电商,肯定离不开商品,而附带商品有各种计数(喜欢数,评论数,鉴定数,浏览数,etc ...
- Java生鲜电商平台-redis缓存在商品中的设计与架构
Java生鲜电商平台-redis缓存在商品中的设计与架构 说明:Java开源生鲜电商平台-redis缓存在商品中的设计与架构. 1. 各种计数,商品维度计数和用户维度计数 说起电商,肯定离不开商品,而 ...
- 微服务架构中的Redis
了解如何将Redis与Spring Cloud和Spring Data一起使用以提供配置服务器,消息代理和数据库. Redis可以广泛用于微服务架构中.它可能是少数流行的软件解决方案之一,你的应用程序 ...
- 庐山真面目之十三微服务架构中如何在Docker上使用Redis缓存
一.介绍 1.开始说明 在微服务器架构中,有一个组件是不能少的,那就是缓存组件.其实来说,缓存组件,这个叫法不是完全正确,因为除了缓存功能,它还能完成其他很多功能.我就不隐瞒了,今天我们要探讨 ...
- 分享一个CQRS/ES架构中基于写文件的EventStore的设计思路
最近打算用C#实现一个基于文件的EventStore. 什么是EventStore 关于什么是EventStore,如果还不清楚的朋友可以去了解下CQRS/Event Sourcing这种架构,我博客 ...
随机推荐
- Extjs 项目中常用的小技巧,也许你用得着(4)---Extjs 中的cookie设置
1.ExtJs设置cookie两种方式 其一:设置cookie如下 saveacct=isForm.getForm().findField('itemselector').getValue(); Ex ...
- C语言Win32 Application 的标题栏图标,任务栏图标,exe文件图标问题
我们这里新建的首先是一个空工程,新建c文件. 因为是空工程,我们没有资源文件,要用到图标资源,首先要新建和导入图标为资源. 选择引入资源,选择要引入的ico文件 完成,重命名保存Script_icon ...
- 无责任Windows Azure SDK .NET开发入门(二):使用Azure AD 进行身份验证
<編者按>本篇为系列文章,带领读者轻松进入Windows Azure SDK .NET开发平台.本文为第二篇,将教导读者使用Azure AD进行身分验证.也推荐读者阅读无责任Windows ...
- JS简单验证
1.验证是否全为数字 2.验证邮箱 3验证手机号 4.验证身份证号 5.验证时间格式 下面是代码,可直接用,有注释 <html> <meta charset="utf-8& ...
- js-ES6学习笔记-module(2)
1.如果想为输入的变量重新取一个名字,import命令要使用as关键字,将输入的变量重命名. import { lastName as surname } from './profile'; 2.im ...
- Django使用多个数据库
一.定义数据库 使用Django的多个数据库的第一步是告诉Django将使用的数据库服务器. 这是使用DATABASES设置完成的. 此设置将数据库别名映射到该特定连接的设置字典,该数据库别名是一种在 ...
- flutter 监控返回键
return new WillPopScope( child: Scaffold( body: new Center( child: new Column( children: <Widget& ...
- android踩坑记录之view.setVisiblity()
问题 在某次做悬浮侧边栏的时候,遇到了一个问题:我用windowManager创建的悬浮侧边栏.点击中心view展开菜单,再次点击则隐藏菜单,如此简单的一个需求,却遇到了奇怪的问题,我没有对view的 ...
- 大数据【四】MapReduce(单词计数;二次排序;计数器;join;分布式缓存)
前言: 根据前面的几篇博客学习,现在可以进行MapReduce学习了.本篇博客首先阐述了MapReduce的概念及使用原理,其次直接从五个实验中实践学习(单词计数,二次排序,计数器,join,分 ...
- nginx 配置 非80 的其他 端口
如果nginx的监听端口不是默认的80端口,改为其他非80端口后,后端服务tomcat中的request.getServerPort()方法无法获得正确的端口号,仍然返回到80端口.在response ...