以下文章非原创。
1. 减少数据库读取
首先我们来看一段代码:
var MyOrderList=new List<MyOrder>();//输出 var mallPayRecordList=DataAccess.MallPayrecorde.GetByPhone(Config.TuanRead, phone);//查询数据库1 foreache(var mallorder in mallPayRecordList) { MyOrder Order =new MyOrder (); Order.MallPayRecorde=mallorder ; Order.MallProjInfo=DataAccess.MallProjInfo.GetByAId(Config.TuanRead,mallorder.AID);//查询数据库2 Order.PlatinumCardUser=DataAccess.PlatinumCardUser.GetByPid(Config.TuanRead,mallorder.PID);//查询数据库3 MyOrderList.Add(Order); }
以前觉得这样写很爽,调理清晰。但是老道的程序员一眼就看出了其中是死穴,这段代码查询数据库的次数极不稳定。
分析:
如果有5条订单,就查询 1+2*5=11次。
如果有10条订单,就查询 1+2*10=21次。
如果有100条订单,就查询 1+2*100=201次。
订单一多数据库就死掉了。。。。。
var MyOrderList=new List<MyOrder>();//输出 var mallPayRecordList=DataAccess.MallPayrecorde.GetByPhone(Config.TuanRead, phone);//查询数据库1 var aids=getAids(mallPayRecordList); var mallProjinfoList=DataAccess.MallProjInfo.GetByAIds(Config.TuanRead,aids);//查询数据库2 var pids=getpids(mallPayRecordList); var platinumCardUserList=DataAccess.PlatinumCardUser.GetByPids(Config.TuanRead,pids);//查询数据库3 foreache(var mallorder in mallPayRecordList) { MyOrder Order =new MyOrder (); Order.MallPayRecorde=mallorder ; Order.MallProjInfo=mallProjinfoList.FirstOrDefault(o=>o.ID==mallorder.AID); Order.PlatinumCardUser=platinumCardUserList.FirstOrDefault(o=>o.ID==mallorder.PID); MyOrderList.Add(Order); }
我们在查询前把所有的用的MallProj和platinumCardUserList一次查出来,剩下的就在内存中操作了。优化后,无论用户有多少条订单,只需要3次查询数据库就搞定了。
实际体验:最原始版订单列表接口每次查询要1分钟才会出来,导致app根本无法打开。优化后3s中就打开了。
2. 使用页面缓存
当我们把数据的查询优化到了极致时,发现某些情况下,接口还是扛不住如洪水般用户的访问,该怎么办呢? 这时候就该用到缓存了。缓存是个很好的东西,最常用的就是页面缓存,使用也很方便,一句话就搞定了。对于访问量很大,但是数据不要求实时性很高的页面,我们可以用下面语句设置页面缓存。
[OutputCache(Duration = 3600, VaryByParam = "*")]
页面输出缓存,非常高效,使用方便,真是居家旅行码农必备。
效果体验:一个接口正常打开需要5s中,当设置好30分钟缓存后,第一个打开还是需要5s中,但是剩下的30分钟会在毫秒之间闪电加载,无需等待。
但是它依然有它的短板所在,例如
1.只能对整个页面加缓存,当某个页面大部分内容没有更新,而小部分内容需要实时刷新时就不好使了。
2 有负载均衡的多台服务器,每台服务器用的缓存周期可能是不一样的。当我们更新了某个数据时,有的服务器更新了,有的还没更新。
当页面缓存江郎才尽时,就该真正的高手Memcache登场了,请往下看:
3. Memcache缓存
memcache是一套分布式的高速缓存系统,由LiveJournal的Brad Fitzpatrick开发,但目前被许多网站使用以提升网站的访问速度,尤其对于一些大型的、需要频繁访问数据库的网站访问速度提升效果十分显著[1] 。这是一套开放源代码软件,以BSD license授权发布。
M非常强大,它可以以键值对的形式存储各种符合要求的对象,并设置超时时间,有了它再也不用担心页面超时了。
使用方法如下:
string cacheName = Verify.GetMd5String("GetRedBagTableByCity" + city); var redBagListByCity = SFCCache.Get(cacheName) as DataTable;//获取缓存 if (redBagListByCity == null || redBagListByCity.Rows.Count < 1) { redBagListByCity = ProjBuTieRules.GetRedBagListByCity(Config.ConnectionString_tuan_Read, min.City); SFCCache.Set(cacheName, redBagListByCity, DateTime.Now.AddMinutes(30));//加入缓存 }
M相对于页面缓存可以多台服务器共用一套缓存,更加灵活可以以在你需要的时候拿来就用,而不用像页面缓存一样要通盘考虑是不是所有的内容都可以缓存。
使用Memcache注意事项:
1. 必须是可序列化的自定义对象,才能缓存
2. Datatable缓存时必须有name
3.默认最大只能存储1M数据,再大了需要配置
4. static全局缓存对象(摸索体验中,尚不成熟)
使用页面缓存或者是Memcache可以大大减少数据库重复调用,可以大大加快接口速度,但是第一次的数据查询还是无法避免的。例如楼盘详情,全国有2W个在执行楼盘,这2w个楼盘详情每一个页面在加载时,第一次读库还是无法避免的。
那么有没有一种办法,可以再一次减少数据库访问呢?这就是我们要介绍的static全局缓存对象。
我们构建一个static的数组,把那些不常改变的值放到全局静态对象里面,当程序任何一个地方要用时可以直接拿来就用,而不用在去查数据,这样数据读取一次就可以一直在有效期内复用了。
public class StaticCacheData { private static readonly Dictionary<string, object> cacheDictionary = new Dictionary<string, object>(); /// <summary> /// 更新静态缓存 jsxu 2016-4-27 19:42:30 /// </summary> /// <param name="key"></param> /// <param name="value"></param> public static void Set(string key, object value) { if (cacheDictionary.ContainsKey(key)) { cacheDictionary[key] = value; } else { if(cacheDictionary.Keys.Count>1000) { //存储太多了,清空一下过期的 cacheDictionary.Clear(); } cacheDictionary.Add(key,value); } } /// <summary> /// 读取静态缓存 jsxu 2016-4-27 19:42:36 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <returns></returns> public static T Get<T>(string key) { if (cacheDictionary.ContainsKey(key)) { return (T) cacheDictionary[key]; } return default(T); } /// <summary> /// 设置缓存key jsxu 2016-4-27 19:42:40 /// </summary> /// <param name="key">key</param> /// <param name="minites">有效期分钟</param> /// <returns></returns> public static string GetKey(string key, int minites) { TimeSpan tspan = DateTime.Now - DateTime.Parse("2000-01-01"); return DateTime.Now.ToString("yyyyMMdd")+"_"+ Verify.GetMd5String(key)+"_"+ (int)(tspan.TotalMinutes/minites); } }
public static List<DirectSellingProjEntity> GetDirectSellingProjList(string databaseConnectionString) { string sql = dspDao.SelectAll + " where starttime<getdate() and endtime>getdate() "; string cachekey = StaticCacheData.GetKey(sql, 30); List<DirectSellingProjEntity> list = StaticCacheData.Get<List<DirectSellingProjEntity>>(cachekey);//静态存储 if (null == list || list.Count < 1) { list = new List<DirectSellingProjEntity>(); DataTable table = DbHelper.Query(databaseConnectionString, sql); foreach (DataRow row in table.Rows) { DirectSellingProjEntity entity = new DirectSellingProjEntity(); dspDao.SetBean(row, entity); list.Add(entity); } StaticCacheData.Set(cachekey,list); } return list; }