设计一个高效的缓存管理服务

摘要:一般大家做的缓存都是实时更新,并且用LRU算法实现缓存过期策略,但当缓存越来越大的时候,对缓存做的线程同步会导致应用的响应便慢。如何更有效的使用缓存,如何提高缓存命中率,如何减少对缓存加锁操作,如何提高缓存的性能,我们来讨论一下。

1、找出活跃数据,我们用一种分离的方式来找出活跃数据,单独写一个提取活跃数据的后台程序从数据库里统计出最近一小时查阅次数最多的前1w篇文章的ID,这些文章肯定是用户最常访问的文章,把这些文章的数据取出来用FTP上传到缓存服务器上,并发消息给缓存服务器通知它有新的缓存数据可用了,因为提取的都是活跃数据,所以这样的数据做缓存命中率会高一些。

2、缓存服务器收到提取活跃数据程序的通知后,从本机磁盘上读取缓存信息加载到内存里,并替换到上次使用的缓存,这样缓存就不是实时更新的,而是相对只读的,每1小时才更新一次,所以使用缓存不需要加锁,只需使用缓存的时候把缓存对象用一个临时变量引用一下,防止在新旧缓存交替的时候被变为null。

3、用一个单独的DB来作为数据版本数据库,里面保存着每篇文章的版本信息,版本信息是int类型的,无论谁修改了某篇文章,都要在版本数据库里让相应的文章版本号加1,写一个版本管理服务提供查询某个文章版本和更新某个文章版本的功能。因为版本数据库的字段大多都是int型,表应该很窄,性能不会很差。

4、用户请求一篇文章的时候,先看缓存服务器有没有,如果没有,直接从数据库里取出来;如果有,取出缓存数据版本号,并从版本服务器上获取该文章真实版本号,如果一直,就使用缓存数据,如不一直,从数据库里取出文章数据,并更新缓存。这里虽然用户的每个请求都要访问版本数据库,但因为版本数据库结构简单,容易优化,所以出现性能瓶颈的的可能性比较小,而如果缓存命中率足够高的话能减少大量对文章数据库的请求。

using System;
using System.Collections.Generic;
using System.Threading;
using System.Diagnostics;

namespace DataCache {

     //可缓存的实体类
     public class Canbecached{public int Version;}
     public class Article : Canbecached { }
     public class CacheItem : Canbecached
     {
         public Article Article;
     }

     //每个文章都在版本数据库里保存版本号,该接口提供查询版本方法
     //如果某个文章修改了,要调用这个接口的UpdateXXVersion方法
     //来更新数据版本,以便让缓存服务器可以得到真实数据的最新的版本
     public interface VersionManager
     {
         int GetArticleVersion(int articleId);
         void UpdateArticleVserion(int articleId);
     }

     //该类用于管理缓存,以及提供缓存使用接口
     //缓存和使用缓存的服务不一定是一个进程,甚至不是一台机器,通过socket
     //或者Remoting来使用及更新缓存你
     internal static class ArticleCacheManager
     {
         private static volatile bool _cacheAvailable = false;
         static Dictionary<int, CacheItem> _cache = new Dictionary<int, CacheItem>();
         public static bool CacheAvailable {
             get { return _cacheAvailable; }
         }
         public static void InitCache()
         {
             _cache = new Dictionary<int, CacheItem>();
             _cacheAvailable = true;
         }

         public static void LoadCache()
         {
             Dictionary<int, CacheItem> cache = readCacheFromDisk();
             _cache = cache; //该操作是线程安全的,不需要lock(_cahce),因为使用_cache的时候都先放到临时变量里再使用的。
         }

         private static Dictionary<int, CacheItem> readCacheFromDisk() {
             throw new NotImplementedException();
         }
         private static void checkCacheAndArticleId(int articleId) {
             if (!_cacheAvailable) throw new InvalidOperationException("Cache not Init.");
             if (articleId < 1) throw new ArgumentException("articleId");
         }

         internal static VersionManager GetVersionManager()
         {
             throw  new NotImplementedException();
         }

         internal static Article GetArticle(int articleId) {
             checkCacheAndArticleId(articleId);
             Dictionary<int, CacheItem> cache = _cache;
             CacheItem item;
             if (cache.TryGetValue(articleId, out item))
                 return item.Article;
             else
                 return null;
         }

         internal static void UpdateArticle(int articleId, Article article) {
             checkCacheAndArticleId(articleId);
             Dictionary<int, CacheItem> cache = _cache;
             CacheItem item;
             if (cache.TryGetValue(articleId, out item))
                 item.Article = article; //这个赋值操作是线程安全的,不需要lock这个Item。
         }
     }

     //从数据库里读取文章信息
     internal static class DBAdapter
     {
         internal static Article GetArticle(int articleId, bool IsUpdateCache) {
             Article article = new Article();
             if(IsUpdateCache)ArticleCacheManager.UpdateArticle(articleId, article);
             throw new NotImplementedException();
         }
     }

     //用来保存一个文章
     public class ArticleItem
     {
         public int ArticleId;
         public ArticleItem(int articleId)
         {
             ArticleId = articleId;
         }

         public Article Article;

         public void Load()
         {
             //1、缓存正在切换到时候直接从DB取数据
             if(!ArticleCacheManager.CacheAvailable)
             {
                 Article = DBAdapter.GetArticle(ArticleId,false);
                 return;
             }

             VersionManager versionManager = ArticleCacheManager.GetVersionManager();

             //2、比较缓存版本和真实数据版本确定是否使用缓存信息
             DataCache.Article article = ArticleCacheManager.GetArticle(ArticleId);
             if(article != null && article.Version == versionManager.GetArticleVersion(ArticleId))
                  Article = ArticleCacheManager.GetArticle(ArticleId); //尽管这里判断了版本,但也有可能取到旧数据,因为当你获取数据版本并决定使用缓存数据的时候,可能恰好用户修改了文章数据,这种情况只要等用户下次刷新一下页面了,用户体验并不是太差。
             else
                 Article = DBAdapter.GetArticle(ArticleId, article != null);//如果article不是null,说明只是缓存数据版本太旧,这时候要把从数据库取出的数据更新到缓存里

         }
     }
     class Program {
         static Dictionary<int, ArticleItem> _articles = new Dictionary<int, ArticleItem>();
         static void Main(string[] args) 
         {
             //初始化缓存
             ArticleCacheManager.InitCache();
             //检查是否有新的缓存可用
             new Thread(checkCacheProc).Start();

             //用户请求一篇文章
             ArticleItem article1 = new ArticleItem(1);
             article1.Load();
         }

         public static void checkCacheProc(Object state)
         {
             Thread.CurrentThread.Name = "Check whether there is a new cache Thread";
             while (true)
             {
                 try {
                     if (newCacheAvailable())
                         ArticleCacheManager.LoadCache();
                     Thread.Sleep(TimeSpan.FromMinutes(60));
                 }
                 catch (Exception ex) {
                     Trace.TraceError("check cache occur an error:"+ ex.Message);
                 }
             }
         }

         private static bool newCacheAvailable() {
             throw new NotImplementedException();
         }
     }
}

不足

1、更新文章的时候需要更新版本数据库,还要用一个事务来保证一致性,需要更改现有代码,并且降低写性能。不过我觉得这比用户更新数据的时候同时通知缓存服务器还是要简单一些,那样更复杂,还是尽量保证设计简单吧,如果版本数据库撑不住了再试试这种方案。

2、因为判断数据版本号和使用缓存数据不是一个原子操作,在这中间数据版本号可能会更新,所以在高并发的情况下,可能给用户显示了比较旧的数据,只有用户再次刷新才会发现文章版本号变了而使用最新数据,这里就的牺牲用户体验换取性能了。

时间: 2024-09-25 02:29:29

设计一个高效的缓存管理服务的相关文章

设计一个高效的缓存管理服务 C#

一直以来,我都发现程序的运行速度不够理想.通过查代码,发现程序对数据库的访问非常频繁,而且检索出来的数据量比较大.为了让程 序运行快起来,我想对程序采用适当的缓存方法. 我在C#尝试了5种方法进行数据缓存,具体如下: (如有遗漏,错误欢迎大家指正,欢迎提建议.) 1:Session方法:此方法是针对于每个用户来的,如果用户量比较大,那么建议不要采用此方法,否则会大量耗尽服务器资源. 2:Cache方法: 2.1:对于每个用户来说访问的数据最好是一致的,否则要用不同的key标识不同的缓存. (要缓

想设计一个高效点的考试自动评分模块

问题描述 想设计一个高效点的考试自动评分模块,大家一起给点思路呗~!!!大概流程是1.题库随机出题[/size]想设计一个高效点的考试自动评分模块,大家一起给点思路呗~!!!,大家一起给点思路呗~!!!大概流程是1.题库随机出题2.考试结束提交试卷3.自动评分不用一条一条查询数据库做比较,有没有高效点的评分模式,希望大家可以一起讨论下,给一些设计思路 解决方案 解决方案二:引用楼主zhuoyi1111的回复: 想设计一个高效点的考试自动评分模块,大家一起给点思路呗~!!!大概流程是1.题库随机出

设计一个真正的企业级管理软件要怎么架构?

问题描述 企业级:我的意思是,一个至少200人同时在线,操作不同模块,并频繁数据交互,偏业务逻辑而非算法.架构:WinForm程序要应用哪些技术,大体思路,数据库要应用哪些技术.当前一个人开发,后期业务拓展,可能协作开发,编程之初要注意什么...等等目前我能够知道的是:1.WinForm中使用的技术:接口,基类,事件,多线程,MDI窗口,用数据库保持连接的方式查询在线用户信息,服务器端没有设计程序.2.数据库:视图,索引,RowNumber分页,存储过程,链接服务器(因为担心服务商不提供,已经放

定时刷新缓存管理器 IRefreshableCacheManager --ESBasic 可复用的.NET类库(16)

1.缘起:     为了提升系统的性能或减轻数据库的压力等原因,我们经常在系统中使用缓存来把那些经常使用的数据保留在内存中.如果因为某些原因,缓存中这些经常使用的数据不能及时与数据源进行同步更新,那么采用定时刷新缓存中的数据有可能就是一种合适的选择.     如果你的缓存是定时刷新,那么你就需要自己为其维护一个定时器或循环引擎.如果你的系统中像这样定时刷新的缓存有多个,而且每个缓存定时刷新的时间间隔又要求不一样,那么,使这些缓存按照你预想的情况进行运转,你就需要花费一些气力.     我设计了定

《PHP精粹:编写高效PHP代码》——3.5节设计一个Web服务

3.5 设计一个Web服务当你创建一个Web服务时有一些关键点你必须牢记.本节将贯穿创建一个高效实用的服务时需要关注的要点.首先要决定你将采用哪种服务形式,如果你的服务和表述数据结合很紧密,你可能会选择RESTful服务.对于计算机之间的数据交换,你可能选择XML-RPC或SOAP,特别是在你确信SOAP已被人们透彻理解的企业环境下.当我们从JavaScript传输异步请求或者传输数据到移动设备时,JSON也许是一个更好的选择.当你使用Web服务时,要始终牢记用户总会将一些毫无意义的内容传入服务

如何才能打造一个高效的面向服务架构?

[编者按]在"著名的推特论战:Microservices vs. Monolithic"一文中,我们曾分享过Netflix.ThougtWorks及Etsy工程师在Microservices上的辩论.在看完整个辩论过程后,或许会有一大部分人认同面向服务这个架构体系.然而事实上,Microservices的执行却并不简单.那么究竟如何才能打造一个高效的面向服务架构?这里我们不妨看向MixRadio首席架构师Steve Robbins的分享. 以下为译文 MixRadio提供了一个免费的音

为Azure添一个管家,微软收购云管理服务MetricsHub

近日,微软宣布已收购了一家提供云管理服务的公司MetricsHub,从而为微软自家云平台 Windows Azure 添了一个管家. 伴随收购信息微软还宣布,即日起 Windows Azure 用户将免费获得预览版的 MetricsHub 自动云监测服务,帮助用户更高效地管理自己的云服务.另外,MetricsHub 公司同时也宣布,公司所有付费用户将直接转成免费计划. 事实上,MetricsHub 是微软自家 Windows Azure 加速器孵化出的项目.微软的这个加速计划是和著名孵化器 Te

设计一个论文提交评价系统,怎样管理各种角色

问题描述 设计一个论文提交评价系统,怎样管理各种角色登陆,记录登陆后各个页面的登陆者角色,以及登陆后访问的权限就是角色管理的入门级问题,麻烦各位高手帮忙详细解释一下实用的解决方案 解决方案 解决方案二:设置userType属性,每个页面进入跟操作前都验证该用户是否有操作权限一般简单点的话用session解决方案三:做一个权限管理系统吧角色表里面放网站的权限名称如管理员超级管理员资源表资源的名称,页面如果作业查看aaa.aspx权限对应的表角色ID资源ID能进行的操作比如浏览,修改,删除这样能把权

新手-设计一个用于管理的人员类

问题描述 设计一个用于管理的人员类 设计一个用于人事管理的People(人员)类 考虑到通用性,这里只抽象出所有类型人员都具有的属性:number(编号).sex(性别).birthday(出生日期).id(身份证号)等.其中"出生日期"声明为一个"日期"类内嵌子对象.用成员函数实现对人员信息的录入和显示.要求包括:构造函数和析构函数.拷贝构造函数.内联成员函数.类的组合. 解决方案 http://blog.sina.com.cn/s/blog_718f727801