性能优化
以下文章非原创。
1. 减少数据库读取
首先我们来看一段代码:
1 2 3 4 5 6 7 8 9 10 | 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次。
订单一多数据库就死掉了。。。。。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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. 使用页面缓存
当我们把数据的查询优化到了极致时,发现某些情况下,接口还是扛不住如洪水般用户的访问,该怎么办呢? 这时候就该用到缓存了。缓存是个很好的东西,最常用的就是页面缓存,使用也很方便,一句话就搞定了。对于访问量很大,但是数据不要求实时性很高的页面,我们可以用下面语句设置页面缓存。
1 | [OutputCache(Duration = 3600, VaryByParam = "*" )] |
页面输出缓存,非常高效,使用方便,真是居家旅行码农必备。![]()
效果体验:一个接口正常打开需要5s中,当设置好30分钟缓存后,第一个打开还是需要5s中,但是剩下的30分钟会在毫秒之间闪电加载,无需等待。
但是它依然有它的短板所在,例如
1.只能对整个页面加缓存,当某个页面大部分内容没有更新,而小部分内容需要实时刷新时就不好使了。
2 有负载均衡的多台服务器,每台服务器用的缓存周期可能是不一样的。当我们更新了某个数据时,有的服务器更新了,有的还没更新。
当页面缓存江郎才尽时,就该真正的高手Memcache登场了,请往下看:
3. Memcache缓存
memcache是一套分布式的高速缓存系统,由LiveJournal的Brad Fitzpatrick开发,但目前被许多网站使用以提升网站的访问速度,尤其对于一些大型的、需要频繁访问数据库的网站访问速度提升效果十分显著[1] 。这是一套开放源代码软件,以BSD license授权发布。
M非常强大,它可以以键值对的形式存储各种符合要求的对象,并设置超时时间,有了它再也不用担心页面超时了。
使用方法如下:
1 2 3 4 5 6 7 | 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的数组,把那些不常改变的值放到全局静态对象里面,当程序任何一个地方要用时可以直接拿来就用,而不用在去查数据,这样数据读取一次就可以一直在有效期内复用了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | 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); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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; } |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· 面试官:你是如何进行SQL调优的?