带你走进缓“.NET技术”存世界

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介:   我们搞程序的多多少少都了解点算法。总体来讲,算法是什么?算法就是“时间”和“空间”的互换策略。我们常常考究一个算法的时间复杂度或空间复杂度,如果我们有绝对足够的时间或空间,那么算法就不需要了,可惜这种条件是不存在的,只是在某些情况下相对来说我们不用去考虑其中一个。

  我们搞程序的多多少少都了解点算法。总体来讲,算法是什么?算法就是“时间”和“空间”的互换策略。我们常常考究一个算法的时间复杂度或空间复杂度,如果我们有绝对足够的时间或空间,那么算法就不需要了,可惜这种条件是不存在的,只是在某些情况下相对来说我们不用去考虑其中一个。今天我们讨论的“缓存”,自然就是“用空间换时间”的算法。 缓存就是把一些数据暂时存放于某些地方,可能是内存,也有可能硬盘。总之,目的就是为了避免某些耗时的操作。我们常见的耗时的操作,比如数据库的查询、一些数据的计算结果,或者是为了减轻服务器的压力。其实减轻压力也是因查询或计算,虽然短耗时,但操作很频繁,累加起来也很长,造成严重排队等情况,服务器抗不住)

  概念性的东西暂就不说了,说多了都是故事。现在我们来谈谈各种缓存。

  初学.NET的朋友开始就会接触到DataSet类,云里雾里的看着DataSet的例子程序,也不管是咋回事,用就是了。其实DataSet就是缓存,当我们去读取一段数据集合的时候,如果每读取一条数据就处理一条的话,那么我们的程序和数据库会一直连接着。假如处理一条数据的耗时可以忽略不计,或者只有你一个人使用这个数据库的话,那么数据库一直连着也无所谓,我们写代码完全可以不用DataSet类。但是事实上不耗时不可能的,如果耗时严重的话,就会一直占用这数据库连接,直到我们处理完毕。如果这种查询过多,连接数就会占用过多,而且数据库在某些操作时会锁住表,这就会造成其他的请求等待,会出现查询超时,程序异常等现象。所以,我们必须先把数据拿出来,再对这些数据进行相关的处理,尽早的关闭数据库连接,好让数据库处理其他的请求。 所以,适时地选用DataSet或DataReader是比较重要的(说明:DataReader就是hold住连接的读取方式)。

  你可能会迷惑,不知不觉中使用了缓存(DataSet),这都是.net帮你完成的事。可是,你可能还是不太清楚该如何使用缓存,或者说何时使用缓存。不用着急,我们一一来看。

  上面说过,我们缓存的数据无非就是一些数据库的查询、计算结果和频繁查询。那么,我们在实际开发中会碰到哪些这种数据呢? 其实仔细想想这是非常常见的,比如用户登录后的个人资料,当他每次点击连接后造成页面刷新,我们总不能都要去重新查询数据库吧?我们常常用Session来存储这个人的信息,当他退出系统后我们把Session清理掉,所以Session也是缓存,只不过他也是.NET给我们提供好的类,sorry,我又举了一个你不想看到的例子,哈哈。其实Session是私有化的数据,Session的数据访问必须通过SessionID(详情我就不多言了,大家google下),还不足以说明缓存的意义。如果把这个问题延伸下去,假如我们开发的是一个多用户的Blog系统, 每当我们访问其中一个博客时都要去查询这个博主的资料,假如A和B同时访问一个博客时,最理想的状态就是只查询一次,而不是两个人都去访问数据库!是不是呢?其实。。。是也不是!(故事里的事,说是就是,不是也是;说不是就不是,是也不是。 :)。之所以说不是,是因为假如我们的博客网站每天就几个人访问,而且一直发展不起来,我们就没必要用缓存,因为使用缓存带来了更多的开发复杂度,因为每当我们去更新博主的资料的时候不单单要更新数据库的信息,我们还要去处理缓存。但是如果我们的博客访问量非常大,就像博客园似的,如果再不缓存,那数据库服务器早就Gameover了:),那么现在就来看怎么用缓存的吧。

  .Net Framework提供了现成的缓存类供我们使用,常见的是 System.Web.HttpRuntime.Cache。每当我们去执行 BlogDataProvier.GetBlogInfo()方法时(假定这个方法是我们获取博主信息的方法,顾名思义嘛),需要在查询之前先从缓存获取数据,假如数据不存在的话,再去数据库获取,并且把得到的结果存入缓存,并且返回该结果既可。下面我把这个方法的伪代码写出来,好让从来没用过缓存的朋友大致了解一下。

 
  
public class SqlDataProvider
{
public static object GetBlogInfo( string username)
{
// 这里是从数据库获取BlogInfo
return null ;
}
}

public class BlogDataProvider
{
public static object GetBlogInfo( string username)
{
var cacheKey
= " Blog_ " + username;
var blog
= CacheHelper.Get(cacheKey);
if (blog == null )
{
blog
= SqlDataProvider.GetBlogInfo(username);
CacheHelper.Set(cacheKey, blog);
}
return blog;
}
}
public class CacheHelper
{
public static object Get( string key)
{
return System.Web.HttpRuntime.Cache.Get(key);
}
public static void Set( string key, object value)
{
System.Web.HttpRuntime.Cache.Insert(key, value);
}
}
缓存,两个字道出了其实际意义,一个是“存”,我们刚刚存了;另一个是“缓”,暂缓,缓存一般只是用来暂时存储,其命运都会被删除或替换掉,所以缓存有个时效问题。如果你说你的数据永远都不会过期,那么说真的,我建议你直接写在代码里就可以了。 

  上面的例子让我们了解到了HttpCache类。看来我们可以用它来解决绝大部分的缓存问题,主要是公共数据的缓存(所谓公共数据就是你我都可以访问的同一数据)。希望新手朋友捧着MSDN仔细学习该类的用法,真的很重要哦,不是吗?

  开始我们说了“拿空间换时间”,目前只提到了缓存一些频繁查询的情况,牺牲空间缓存时间的明显些的例子有吗?没问题,你看好咯!

  说之前先插一句,我们公司现在在招人,其中一道笔试题是介绍一下List<T>和Dictionary<TKey,TValue>的区别和用途。很遗憾,面试了很多人,只有一个同学回答的到位,其他的说什么的都有。你想好怎么回答了吗?:)如果你看了下面发现和你现在想的一致而且你还需要找一份有挑战的工作的话,给我消息哦。

  其实用List<T>,Dictionary<TKey,TValue>泛型就是用来迷魂人的,哈哈,就会有些同学往泛型上面扯,结果上当咯,我完全可以用ArrayList和Hashtable来问。

  List是什么数据结构?数组!而且是动态的数组,之所以动态就是可以视情况动态申请空间。Dictionary是什么结构?有的同学回答是字典。字典是什么数据结构? 散列表!散列,一听这名字就知道是散开分布的数据表。怎么个“散”法? 自然是按照Key来散,每个Key对应一个Value,所以我们常叫做“键值对”,Key和Value是成对的。我们把Dictionary看作是一个数组,那么每个Key的hash值(什么是hash值?在.net里任何类型都有GetHashCode方法,返回int值,有木有),便是数组的下标,而该数组的元素值就是Value!所以我们在获取Dictionary的某个Key的Value时,速度是非常快的,可以直接通过已知的下标拿到值,这个时间复杂度是O(1)。快不快啊?好快好快。但是,你有木有想到,所有的Key的hash值是按顺序来的吗?显然不是,鬼知道你用的什么key,所以,Dictionary的这个数组很长很长,浪费了很多空位置,所以,那就是 空间 换&n上海企业网站设计与制作bsp;时间。当然GetHashCode的算法不同,Key对应的值的分布也有区别,有的比较紧密有的比较松散,常见的算法比如一致性hash算法。

dictionary的实际内存分布

  如上图所示,dict的分布是不紧凑的,牺牲了很多空间,但可以最快速的找到数据,所以dict或hash或map等,不管什么叫什么类,总之都是hashtable,它们的用途主要就是查询。所以,如果我们把博客按用户名作key缓存起来的话,用户访问博客时都是使用的username,所以我们甚至不需要blogId,就可以拿到博主的信息,根本没走数据库。

  而list这种排列紧凑的数据集合一般用于批处理。当然还有兼顾空间和速度的数据结构,那就是树结构,在查找时不需要所有数据都进行遍历,时间复杂度一般是O(logn),而且空间是紧凑的,采用的是链表结构,而不是紧凑的数组。所以在时间和空间上都不比前两者,但用途却十分广大,我们所用的数据库的索引基本上都是用的树。这样既保证了占用空间小,查询的速度也不慢。

  上面这一段我们介绍了hash表的基本原理,现在我们明白了缓存的优势,在实际的项目使用中,我们除了使用系统提供的Cache类以外,完全可以自己尝试写缓存类,为什么不呢?呵呵。我们把一个变量斯static,然后再public,就等于是全局变量了,我们可以到处访问到他,而且我们还要用dict,因为他足够快!还不快动手去写一个,回来再接着看!

  刚才提到了“缓”字,缓也有不同的策略,比如最常见的按时间缓存,在单位时间内该数据有效,每当访问时都要判断缓存的数据是否过期,再决定Get还是Remove。除了时间策略,还有使用热度策略,由于内存有限,所以我们的缓存也不是无限申请的,是时候限制长度了。限制了长度就意味着有人能进来就得有人要出去。这就是Remove策略。我们可以对所有的缓存打上标记,来标记他的热度,每次添加缓存的时候把热度最低的缓存剔除掉(假如已经达到限制的话)。每次获取缓存的时候给该缓存热度+1。这是多人性化的设计,不是吗上海网站建设? 我上篇博文中已经贴出了这类的代码。有兴趣的朋友给你们个传送门

  我们继续用博客园作例子,我们知道博客园的访问量已经很大了(具体多大,俺不知道,反正以前发表评论经常超时,官方团队解决后还发表博文说咋解决的,结果评论里一大票同学都说怎么不用缓存阿:)。  

  当网站访问量达到一定程度后,一台机器很难处理太多的httprequest,这个时候我们必须使用多台机器。假如你的程序没同时跑在多台机器上的话,你对缓存的理解恐怕不会很深,因为谁都要会这种经历:哎呀 sessio不能分布式阿? 哎呀妈呀,我的缓存不能在两台机器上阿,这可咋整?!

  其实这也不能怪你,要怪就怪微软吧。因为IIS,我们的web程序驻留在一个进程里,每个httprequest会有一个线程来处理,所以你甚至都没用过多线程。害人啊,哈哈。但随着项目经验的增加,特别是大项目的历练后,也没什么了。之所以说是微软的错,是因为人家php阿,ruby阿,人家的服务端(apache,nginx等)都是多进程的。每个httprequest一个进程,总共开几十个进程,处理并发。多进程就意味着数据共享问题,就像我们多台机器的情况一样。 这时候需要借助一个共享缓存进程来供其他的web服务进程来访问获取缓存。 这就是下面要说的 分布式缓存。

  如果说两三年前你不知memcached为何物,或许情有可原,那时候还流行自己写windows service。但现在满世界的NoSQLMongoDB,Memcached,Redis,你再不知道的话,真该说多看看博客吧;看看新技术,你已经落后一个时代了。

  上面提到的这个名词都是玩缓存的主。NoSQL是个新技术,NoSQL DB现在很多种,MongoDB就是一种,MongoDB是介于传统关系型数据库和内存数据库的杂交数据库,现在也算是很热门的数据库。MemCached是著名的分布式缓存服务,而Redis(Remoting Dictionary Service),你懂了吧?!我们的缓存服务器可以用memcached或redis,Memcached是纯内存的,重启进程会丢失所有缓存,而redis可以把数据写到硬盘里,各有各的优点吧。Redis更适合存经过计算过的数据。而且Redis支持丰富的数据类型(list\set\hash\string),这要比memcached更灵活些。 他们都有.net的Driver,还有相关的Example和UnitTest,可以官网下载看看。

  关于Redis的使用可以看代震军大虾的http://www.cnblogs.com/daizhj/archive/2011/02/21/1959511.html 

  随着硬件的发展,内存的不断增加,缓存的应用越来越多,现在各种NoSQL数据库应用而生,我们也要紧跟脚步,学习新的理念。关于NoSQL我这里也不多介绍了,自己搜索。

  其他的缓存,比如页面的缓存(OutputCache)我这里就不作介绍了,缓存其他同学补充。谢谢各位阅读。欢迎大家交流,交流的方式希望是和谐的,最好不要有带味的评论,带来不好的讨论氛围,说错误的希望大家提出。

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
3月前
|
人工智能 前端开发 Devops
.NET技术在现代开发中的作用:.NET技术的核心价值、在现代应用开发中的实际应用、以及面临的挑战与未来趋势。
.NET技术是软件开发领域的核心力量,本文从其核心价值、实际应用及未来挑战三方面进行探讨。它支持多种语言,提供强大的开发工具和丰富的类库,并具备跨平台能力。在现代应用开发中,.NET广泛应用于企业级系统、Web应用、移动应用、云服务和游戏开发等领域。面对性能优化、容器化、AI集成等挑战,.NET持续创新以适应不断发展变化的技术环境。
60 4
|
3月前
|
人工智能 开发框架 .NET
.NET技术的强大功能:.NET技术的基础特性、在现代开发中的应用、以及它如何助力未来的软件开发。
.NET技术是软件开发领域的核心支柱,以其强大功能、灵活性及安全性广受认可。本文分三部分解析:基础特性如多语言支持、统一运行时环境;现代应用如企业级与Web开发、移动应用、云服务及游戏开发;以及未来趋势如性能优化、容器化、AI集成等,展望.NET在不断变化的技术环境中持续发展与创新。
115 4
|
3月前
|
人工智能 物联网 开发者
.NET技术在现代软件开发中的应用愈发广泛和深入
.NET技术是软件开发的关键支柱,本文分为三部分探讨其创新应用:最新进展如.NET 5/6统一平台、性能提升、跨平台支持增强、云集成优化及开源社区贡献;应用场景涵盖微服务架构、物联网、AI/机器学习、游戏及移动应用开发;未来发展潜力在于持续性能优化、云原生支持、新兴技术集成、生态扩张及教育培训加强。.NET正以其强大适应性和创新潜力引领软件开发的新方向。
47 3
|
3月前
|
人工智能 开发框架 .NET
如何掌握.NET技术,引领开发前沿:.NET技术的核心能力、在现代开发中的应用实践、以及如何通过.NET技术实现持续创新。
.NET技术已成为软件开发不可或缺的部分,本文分三部分探讨:核心能力如多语言支持、统一运行时环境、丰富的类库及跨平台能力;现代开发实践包括企业级应用、Web与移动开发、云服务及游戏开发;并通过性能优化、容器化、AI集成等方面实现持续创新,使开发者站在技术前沿。
63 3
|
28天前
|
人工智能 开发框架 C#
C#/.NET/.NET Core技术前沿周刊 | 第 6 期(2024年9.16-9.22)
C#/.NET/.NET Core技术前沿周刊 | 第 6 期(2024年9.16-9.22)
|
27天前
|
人工智能 开发框架 Cloud Native
C#/.NET/.NET Core技术前沿周刊 | 第 9 期(2024年10.07-10.13)
C#/.NET/.NET Core技术前沿周刊 | 第 9 期(2024年10.07-10.13)
|
27天前
|
数据可视化 NoSQL C#
C#/.NET/.NET Core技术前沿周刊 | 第 8 期(2024年10.01-10.06)
C#/.NET/.NET Core技术前沿周刊 | 第 8 期(2024年10.01-10.06)
|
28天前
|
设计模式 开发框架 C#
C#/.NET/.NET Core技术前沿周刊 | 第 4 期(2024年9.1-9.8)
C#/.NET/.NET Core技术前沿周刊 | 第 4 期(2024年9.1-9.8)
|
2月前
|
人工智能 前端开发 开发工具
解读.NET 技术的开发潜力
本文全面介绍了.NET技术在软件开发领域的核心优势、创新应用及面临的挑战。.NET以其统一的开发平台、强大的工具和跨平台能力,成为企业级应用、Web应用乃至游戏开发的理想选择。然而,在性能优化、容器化及AI集成等方面仍需不断突破。通过积极拥抱开源和社区驱动模式,.NET将持续推动软件开发的进步。
55 1
|
2月前
|
监控 网络协议 API
.NET WebSocket 技术深入解析,你学会了吗?
【9月更文挑战第4天】WebSocket 作为一种全双工协议,凭借低延迟和高性能特点,成为实时应用的首选技术。.NET 框架提供了强大的 WebSocket 支持,使实时通信变得简单。本文介绍 WebSocket 的基本概念、.NET 中的使用方法及编程模型,并探讨其在实时聊天、监控、在线游戏和协同编辑等场景的应用,同时分享最佳实践,帮助开发者构建高效实时应用。
137 12