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

📅 2026/7/5 3:41:27
概念性的东西暂就不说了,说多了都是故事。现在我们来谈谈各种缓存。
初学.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()方法时假定这个方法是我们获取博主信息的方法顾名思义嘛需要在查询之前先从缓存获取数据假如数据不存在的话再去数据库获取并且把得到的结果存入缓存并且返回该结果既可。下面我把这个方法的伪代码写出来好让从来没用过缓存的朋友大致了解一下。publicclassSqlDataProvider{publicstaticobjectGetBlogInfo(stringusername){//这里是从数据库获取BlogInforeturnnull;}}publicclassBlogDataProvider{publicstaticobjectGetBlogInfo(stringusername){varcacheKey Blog_ username;varblog CacheHelper.Get(cacheKey);if(blog null){blog SqlDataProvider.GetBlogInfo(username);CacheHelper.Set(cacheKey, blog);}returnblog;}}publicclassCacheHelper{publicstaticobjectGet(stringkey){returnSystem.Web.HttpRuntime.Cache.Get(key);}publicstaticvoidSet(stringkey,objectvalue){System.Web.HttpRuntime.Cache.Insert(key, value);}}缓存两个字道出了其实际意义一个是“存”我们刚刚存了另一个是“缓”暂缓缓存一般只是用来暂时存储其命运都会被删除或替换掉所以缓存有个时效问题。如果你说你的数据永远都不会过期那么说真的我建议你直接写在代码里就可以了。上面的例子让我们了解到了HttpCache类。看来我们可以用它来解决绝大部分的缓存问题主要是公共数据的缓存所谓公共数据就是你我都可以访问的同一数据。希望新手朋友捧着MSDN仔细学习该类的用法真的很重要哦不是吗开始我们说了“拿空间换时间”目前只提到了缓存一些频繁查询的情况牺牲空间缓存时间的明显些的例子有吗没问题你看好咯说之前先插一句我们公司现在在招人其中一道笔试题是介绍一下ListT和DictionaryTKey,TValue的区别和用途。很遗憾面试了很多人只有一个同学回答的到位其他的说什么的都有。你想好怎么回答了吗如果你看了下面发现和你现在想的一致而且你还需要找一份有挑战的工作的话给我消息哦。其实用ListTDictionaryTKey,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的这个数组很长很长浪费了很多空位置所以那就是 空间 换 时间。当然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阿人家的服务端(apachenginx等)都是多进程的。每个httprequest一个进程总共开几十个进程处理并发。多进程就意味着数据共享问题就像我们多台机器的情况一样。 这时候需要借助一个共享缓存进程来供其他的web服务进程来访问获取缓存。 这就是下面要说的 分布式缓存。如果说两三年前你不知memcached为何物或许情有可原那时候还流行自己写windows service。但现在满世界的NoSQLMongoDBMemcachedRedis你再不知道的话真该说多看看博客吧看看新技术你已经落后一个时代了。上面提到的这个名词都是玩缓存的主。NoSQL是个新技术NoSQL DB现在很多种MongoDB就是一种MongoDB是介于传统关系型数据库和内存数据库的杂交数据库现在也算是很热门的数据库。MemCached是著名的分布式缓存服务而Redis(Remoting Dictionary Service)你懂了吧我们的缓存服务器可以用memcached或redisMemcached是纯内存的重启进程会丢失所有缓存而redis可以把数据写到硬盘里各有各的优点吧。Redis更适合存经过计算过的数据。而且Redis支持丰富的数据类型(list\set\hash\string)这要比memcached更灵活些。 他们都有.net的Driver还有相关的Example和UnitTest可以官网下载看看。