关于服务器缓存的思考

  咱们在开发中,常常会用到各类缓存,好比Session、Application、HttpRuntime.Cache、Redis、Memcached、MongoDB、Riak等。而通常项目中使用缓存时,都是比较初级的,大多都是常见的Key-Value方式,经过依赖、时间、同步更新或直接删除方法来管理缓存的过时。固然网上对于缓存的介绍绝大部分都是这方面的,而对于多级缓存、缓存与缓存相互关联、表记录与多缓存关联、后端缓存与前端页面缓存关联、缓存名称动态生成的缓存与其余缓存联动处理、频繁更新的缓存与其余缓存联动问题......等等不一样状况下该若是去管理这些缓存的知识点,我在园子里找了半天也没有看到......而最近本身开发的项目中就碰到了缓存管理上的问题,因此发这篇贴子同你们讨论一下有什么更好的解决方案。前端

 

  随着项目参与人员数量的增长,你们经验的不一样和对缓存的认知不同,而项目为了达到在生产环境上能处理更大的并发和良好的性能,缓存的使用也愈来愈普遍了。项目在老板、运营部门和项目经理的推进下,新功能、新需求不断的推陈出新,代码与复杂度也几何级的爆增起来,缓存的使用几乎充斥在全部代码的调用当中,因为没有专门写一个处理插件对它进行统一管理,形成缓存管理开始有些混乱。算法

  好比对运行中的系统在某些状况下执行了清空所有缓存时,偶尔会发生一些小异常(有些数据读取不正常);对同一结果重复使用缓存(A同事建立了一个缓存来存储H任务执行结果的,而B同事不知道A同事建立过,也建立了一个),浪费内存空间;多个缓存依赖同一个缓存值的变更,某些人因为一些须要修改了所依赖缓存的名称以及所影响的一些缓存时,个别非本身编写的缓存没有处理到或忘记处理了;某些缓存存储内容依赖临时表来建立的,缓存名称有规律但不固定,而另一些缓存的内容是根据这些缓存来计算的,当这些缓存更新时如何能自动同步所依赖它的缓存......后端

  固然上面这些状况或还有其余的状况单独编写代码来处理确定能够实现,只须要花多一些时间而已,问题是若是系统很庞大后,充斥差各类交叉关联的缓存时,它们已像蜘蛛网同样,动一发而牵动全身,写这些处理代码一个没考虑好就会影响到其余内容。何况有不少时候更新临时表记录时,有些缓存名称是不固定的(根据某些规则关联到其余表记录或日期等方式生成的缓存名称),代码并不可能智能识别要同步更新那些缓存。因此编写一个强大的自动化缓存处理插件也势在必行了。缓存

  相信博客园团队和其余一些大型的网站都有着本身一套完善的缓存管理办法,因为本身知识广度还不足够,暂时没有在网上发现有一套完善、高效的解决方案能够借鉴,只好本身来想办法解决,因此先抛出一块砖头,看可否引来一堆美玉了,嘿嘿...服务器

 

  下面咱们先来了解一下缓存中的一些分类与名词说明并发

  按名称的命名可分为:框架

  固定名称:一般以表名、字段名、功能名称、前几项的组合......等按各人的喜爱来进行命名,调用时也直接方便函数

  有固定前缀(单个或多个可变后缀或可变后缀+固定后缀组合等):表名+记录Id、临时表名(表名按必定规则进行变更,好比后缀为年月、关联表的Id等)、功能名称+编码......调用时须要动态传入指定的参数,在不知道参数的状况下没法对该缓存进行操做性能

 

  按缓存依赖内容可分为:学习

  依赖指定表:指定表记录增长、修改、删除、更新时,须要同步更新该缓存内容

  依赖指定表中的某些或某条记录:同上

  依赖多个表数据:同上

  依赖指定字段值:默字段值改变时,同步修改(主要用于更新频率比较高的字段,好比页面点击计数等,若是须要用到缓存的,须要独立出来存储,以避免更新时执行同步清除功能)

  依赖其余缓存:指定缓存值改动时,须要同步修改所依赖它的其余缓存值(好比依赖某些计算结果或状态值;存储某些临时记录等)

  ......

 

  按影响缓存值的操做可分为:

  数据表记录的添加、修改、删除;其余缓存值的更新变化;某些计算结果的变化等

  对于所依赖的内容变化后,相关缓存就须要同步更新,这样又可分为实时同步和延时同步等方式

  缓存更新策略经过有:实时更新(主要针对记录级别缓存,直接同步更新指定记录;固然也能够整表更新,但这样对程序执行性能有较大的影响)、超时检测(好比缓存依赖其余缓存时,设置一个最后更新时间与获取时间,经过比较两个时间来肯定缓存是否过时)、绝对时间过时(为缓存设定过时时间)、动态时间过时(缓存被访问后过时时间顺延)等。

 

  按缓存数据集合大小分:

  单值、单条记录、小型数据集合、中型数据集合、大型数据集合、超大型数据集合

  对于缓存管理,数据集越小则存取与转换速度越快,因此当数据集合过大时,就必须进行分割,将集合尽可能分红小块,提高缓存使用性能

 

  按缓存更新频率分:

  固定值(指的是某些配置信息,存储进缓存后它的值就再也不变化)、偶尔更新、常常更新、频繁更新

 

  按缓存级别分:

  无分级缓存、二级缓存

 

  其余:

  数据缓存、页面缓存......

 

  通常来讲,大部分人使用缓存都是直接key-value,这样种操做简单方便,无需太多的算法去处理。而这样操做对于记录集合比较大的数据(固然不能直接缓存大型或超大型数据)来讲,频繁的进行数据存取转换也会消耗很多资源,因此有时须要在这个基础上再加个二级缓存,将NOSQL缓存中读取出来的数据载入IIS缓存中,程序直接编写代码调用,只有相关值更改时再从新加载一次,这样就减小了对大数据转换的性能损耗,固然程序的复杂度就大大提高了不少。

  对于使用二级缓存或依赖其余缓存的缓存来讲,常常更新或频繁更新影响是最大的,程序写的很差直接会形成性能几何级的降低(由于每一次更新都须要同步更新相关的全部缓存)。

 

  目前系统使用缓存情况

  目前个人框架使用的是二级缓存,首先是使用Redis缓存存储各类表记录和值,而后对于某些数据量不小,修改很少但使用相对比较频繁的数据,为了减小从Redis缓存中不停的读取出来后进行反序列化操做,会从Redis缓存中读取出来后将它存储到IIS缓存中,当这些数据有更新时会实时同步更新IIS缓存中的数据,这些代码都封装在逻辑层中,统一使用模板生成,方便快捷。

  将业务数据量大的模块进行了分割,天天按不一样属性生成N个临时表,次日凌晨会执行定时任务将对这些临时表进行分析处理,去除无效数据后统一更新到历史表中(历史表按月生成)。业务数据分割后,每一个表的记录量都不多,它们都会存储到相应的缓存中给先后端、服务、Socket等接口进行共同调用处理,目前是不一样服务器全部功能共用一个缓存服务。各类系统服务会将经常使用的数据或记录存储到指定的缓存中,减小跨临时表查询操做或全表数据查询操做。有些功能只使用当天要用到的一些最新数据,旧数据再也不使用不须要参与查询,也会使用单独的缓存来进行存储,缓存存储按固定前缀+有规则的后缀进行管理。

  前端页面则直接缓存在Redis中。

  ......

 

  缓存处理存在问题

  除了前面所讲的缓存问题外,咱们后端更新某些数据时(好比商品资料),就必须清除前端全部页面缓存,全面从新生成(由于不少页面都会展现商品相关信息),因为没有一个综合管理缓存的框架,在更新时就会将一些没必要删除的缓存也同步清除了。而在某些时候缓存被不少其余缓存所依赖时,清除该缓存也会清除一些多余的缓存,而不是精肯定位。对于动态生成的可变后缀的缓存,在某些时候没法传递后缀参数时,将很难同步更新这些缓存内容。

 

  缓存处理解决思路

  对于出现上面的一些问题,在综合考虑后,想写个独立的缓存处理插件来处理这些问题。主要经过配置来将缓存直接绑定数据表、字段、记录Id、关联缓存、页面缓存等关联内容,在这些缓存更新时同步清除对应的缓存模块,以便其余缓存重启缓存载入程序来加载相应数据到缓存中。在清除时有针对性,而不会跨界清空多余的缓存。不知你们有什么好的建议?

 

  下面是个人一些解决思路:

  一、首先缓存插件必须是一个独立的程序

  二、调用必须经过统一的接口来进行处理

  三、缓存关联必须经过配置来实现绑定

  四、缓存命名必须符合必定的规范

 

  具体实现办法:

  获取缓存:Get Cache => Check null => Load => Save(保存时会执行存储数据的检查,这里开发时要当心,避免出现死循环) => Return Cache (即取缓存时必须检查指定缓存是否为空,为空时调用Load接口载入数据到缓存——Load函数功能由操做方实现,使用配置+IoC来调用,IoC配置文件和接口文件能够用T4模板直接生成——,而后将数据存储到缓存中,最后返回所要的缓存;固然若是缓存不为空时直接返回缓存)

  存储数据:Save Cache => Save => Check Relevance => Delete Relevance Cache (即存储数据时,首先将数据保存到缓存中,而后读取配置信息检查该缓存与那些缓存关联,若是存在关联关系的缓存,则同步清除这些缓存,以便下次获取这些缓存时能从新加载)

  删除缓存:Delete Cache => Delete => Check Relevance => Delete Relevance Cache(删除时执行递归调用,按正常来讲,这种关联应该不会太深)

  设置缓存参数:Set (修改缓存插件的一些全局配置)

 

  给外部直接调用的只有Get/Save/Delete,须要外部程序实现的接口暂定为Load这一个,里面实现数据加载的代码

  在配置时,缓存依赖必须单向,避免出现死循环(可写程序检查配置)

  要处理好动态后缀缓存的处理,能经过参数控制智能判断缓存的关联。好比名称为tablename_id的缓存,在执行Load时会将id截取出来传递给操做函数,那么载入时就只加载该id的记录;

  对于更新频繁的数据,好比页面点击计数等,若是须要用到缓存的,须要独立出来存取和更新,以避免更新时执行同步清除功能

  能够经过Set来开启或关闭Load、Delete Relevance Cache功能等

  

 

  因为工做时间繁忙,本随笔断断续续写了好长时间,有些想法和思路没有及时记下来都忘了,暂时想到这么多,思路也不是很成熟,不知你们有什么好的建议?这种处理模式是否存在什么问题?欢迎你们出来拍砖

 

 

 版权声明:

  本文由AllEmpty原创并发布于博客园,欢迎转载,未经本人赞成必须保留此段声明,且在文章页面明显位置给出原文连接,不然保留追究法律责任的权利。若有问题,能够经过1654937@qq.com 联系我,很是感谢。

 

  发表本编内容,为了和你们共同窗习共同进步,有兴趣的朋友能够加加Q群:327360708 ,你们一块儿探讨。

 

  更多内容,敬请观注博客:http://www.cnblogs.com/EmptyFS/

相关文章
相关标签/搜索