带你走进缓存世界

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

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

  初学.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){//这里是从数据库获取BlogInforeturn 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的这个数组很长很长,浪费了很多空位置,所以,那就是 空间 换 时间。当然GetHashCode的算法不同,Key对应的值的分布也有区别,有的比较紧密有的比较松散,常见的算法比如一致性hash算法。

dictionary的实际内存分布2011040511314628.jpg

  如上图所示,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。但现在满世界的NoSQL,MongoDB,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)我这里就不作介绍了,缓存其他同学补充。谢谢各位阅读。欢迎大家交流,交流的方式希望是和谐的,最好不要有带味的评论,带来不好的讨论氛围,说错误的希望大家提出。

转载于:https://www.cnblogs.com/mad/archive/2011/04/05/take_you_into_the_world_of_caching.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/378543.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

霍夫码编码(一种不等长,非前缀编码方式)

霍夫曼编码是一种不等长非前缀编码方式&#xff0c;于1951年由MIT的霍夫曼提出。 用于对一串数字/符号编码获取最短的结果&#xff0c;获取最大的压缩效率。 特点&#xff1a;不等长、非前缀 等长式编码 等长编码&#xff0c;意思是对出现的元素采用相同位数的序号进行标定&a…

JS 获取浏览器信息,给出友情提示,避免部分兼容性问题

最近在做webform,浏览器兼容是个问题,这里我收集了一些获取浏览器信息的资料,可以给一些用户使用时,提示浏览器版本过低,让升级版本用. 这样会给开发的我们,省下很多用来调试兼容性的时间和精力. 本人就是这样想的 ~  检测浏览器及版本使用 JavaScript 检测关于访问者的浏览器…

06-机器学习(Haar+Adaboost实现人脸、人眼检测)

机器学习是什么? 机器学习训练样本特征分类器&#xff0c;通过让机器学习的方式&#xff0c;来达到某种功能的过程 深度学习是什么&#xff1f; 深度学习海量的学习样本人工神经网络 机器学习需要&#xff1a;样本、特征、分类器、对训练后的数据进行预测或检验 人脸样本haar…

Opencv实战【3】——图像修复与图像锐化(darling in the franxx)

目录前言图像修复图像锐化darling in the franxx图片总结前言 前天&#xff0c;在群里看见有人发了这张表情包&#xff1a; 感觉女主有点好看&#xff0c;然后问室友是啥番剧&#xff08;darling in the franxx&#xff09;&#xff0c;然后就去补番了&#xff0c;然后从晚上…

07-机器学习(Hog+SVM实现小狮子识别)

一、SVM支持向量机 什么是SVM支持向量机&#xff1f; SVM支持向量机本质仍是一个分类器&#xff0c;其核心为寻求一个最优超平面最终实现分类&#xff0c;实现分类问题 在寻求超平面的时候有多种方式&#xff0c;可以使用若干条直线或曲线进行分类&#xff0c;这里使用的是直线…

Net Remoting基础篇

一、Remoting基础 什么是Remoting&#xff0c;简而言之&#xff0c;我们可以将其看作是一种分布式处理方式。从微软的产品角度来看&#xff0c;可以说Remoting就是DCOM的一种升 级&#xff0c;它改善了很多功能&#xff0c;并极好的融合到.Net平台下。Microsoft .NET Remoting …

Maven3.0.5代理nexus

Nexus简介 Nexus是Sonatype推出的强大Maven仓库管理器产品&#xff0c;要比以前TSS上介绍的Artifactory要好使用的多&#xff0c;也是一个拆箱即用的Java App&#xff0c;内嵌Jetty容器和Java Wrapper做Windows服务&#xff0c;安装简单到解压然后双击install即可。更详细的帮助…

8253译码电路设计以及初始化编程讲解

先验知识回顾&#xff1a;知识点不清晰的时候可以查询相关知识点。 https://blog.csdn.net/qq_42604176/article/details/105810973 需掌握的主要知识点 1、译码电路设计 2、初始化编程 例题1 在以 8086构成的最大方式系统中&#xff0c;有一片8254的端口地址分别为301H、3…

基于图像处理的数码印花喷墨墨滴形状规范的研究(Python+OpenCV+Mysql)

大体思路&#xff1a;由于墨滴的不同参数会对墨滴的形态产生一定的影响&#xff0c;故如果通过研究墨滴的形态则通过海量的数据就可以大概确定墨滴的各项参数指标的范围。通过OpenCV对墨滴的喷出的形状进行图像处理&#xff0c;对墨滴图像进行一系列的分析&#xff0c;通过一系…

Opencv实战【4】——图片动漫化处理

博主联系方式&#xff1a; QQ:1540984562 微信&#xff1a;wxid_nz49532kbh9u22 QQ交流群&#xff1a;750313950 目录动漫化风格的特点处理手段代码实现效果总结动漫化风格的特点 &#xff08;1&#xff09;动漫中的细节相对少&#xff1b; &#xff08;2&#xff09;动漫中的边…

08-KNN手写数字识别

标签下载地址 文件内容备注train-images-idx3-ubyte.gz训练集图片&#xff1a;55000张训练图片&#xff0c;5000张验证图片train-labels-idx1-ubyte.gz训练集图片对应的数字标签t10k-images-idx3-ubyte.gz测试集图片&#xff1a;10000张图片t表示test&#xff0c;测试图片&…

MFC odbc访问远程数据库

首先&#xff0c;MFC通过ODBC访问数据库&#xff0c;主要使用两个类&#xff0c;一个是CDataBase&#xff0c;一个是CRecordset。第一个是用于建立数据库连接的&#xff0c;第二个是数据集&#xff0c;用来查询的。步骤如下&#xff1a;1.实例化一个CDataBase对象&#xff0c;并…

微机原理——扩展存储器设计

目录【1】存储器的层次结构【2】存储器的分类【3】SRAM1、基本原理&#xff1a;2、结构&#xff1a;3、芯片参数与引脚解读&#xff1a;4、CPU与SRAM的连接方式【4】DRAM1、基本原理&#xff1a;2、结构3、芯片引脚解读&#xff1a;【5】存储器系统设计【6】存储器扩展设计&…

09-CNN手写数字识别

CNN卷积神经网络的本质就是卷积运算 维度的调整&#xff1a; tf.reshape(imageInput,[-1,28,28,1]) imageInput为[None,784]&#xff0c;N行* 784维 调整为 M28行28列*1通道 即&#xff1a;二维转化为四维数据 参数一&#xff1a;等价于运算结果M 参数二&#xff1a;28 28 表示…

Opencv将处理后的视频保存出现的问题

问题描述&#xff1a; 代码运行过程中&#xff0c;imshow出来的每帧的效果图是正确的&#xff0c;但是按照网上的方法保存下来却是0kb&#xff0c;打开不了。 参考的网上的一些方法&#xff0c;均是失败的&#xff0c;具体原因我也不清楚&#xff1a; 1、例如我这样设置&#x…

黑白图片颜色反转并保存

将图像的黑白颜色反转并保存 import cv2 # opencv读取图像 img cv2.imread(rE:\Python-workspace\OpenCV\OpenCV/YY.png, 1) cv2.imshow(img, img) img_shape img.shape # 图像大小(565, 650, 3) print(img_shape) h img_shape[0] w img_shape[1] # 彩色图像转换为灰度图…

家猫WEB系统

现在只放源码在些.为它写应用很简单有空整理文档演示地址:jiamaocode.com/os/ 源码&#xff1a;http://jiamaocode.com/ProCts/2011/04/14/1918/1918.html转载于:https://www.cnblogs.com/jiamao/archive/2011/04/16/2018339.html

plesk 运行不了php,如何在Plesk中使用composer(使用其他版本的PHP运行Composer)

对于基于Plesk的服务器, composer的默认安装将使用系统安装的PHP版本, 而不使用Plesk所安装的任何版本。尽管Composer至少需要PHP 5.3.2, 但是当你尝试在需要特定版本PHP的项目中安装依赖项时, 就会出现问题。例如, 如果你有一个至少需要PHP 7.2的项目, 并且系统的默认PHP安装是…

Error: Flash Download failed - Target DLL has been cancelled

博主联系方式: QQ:1540984562 QQ交流群:892023501 群里会有往届的smarters和电赛选手,群里也会不时分享一些有用的资料,有问题可以在群里多问问。 由于换了新电脑,keil重装了下,然而之前的MCU的支持包没有安装,以及一些其他的问题,导致可以编译但是不能将程序烧录到单片…

我也谈委托与事件

虽然在博客园里面已经有很多关于C#委托和事件的文章&#xff0c;但是为了自己在学习的过程中&#xff0c;加深对委托的理解&#xff0c;我还是决定写一下自己的心得体会。以备他日在回来复习。委托&#xff08;delegate&#xff09;是一个类&#xff0c;但是这个类在声明的时候…