性能优化

以下文章非原创。

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非常强大,它可以以键值对的形式存储各种符合要求的对象,并设置超时时间,有了它再也不用担心页面超时了。
    
               拓展阅读:http://369369.blog.51cto.com/319630/833234/
 
              使用方法如下:           
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;
       }

  

posted @   PanPan003  阅读(318)  评论(0编辑  收藏  举报
编辑推荐:
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· 面试官:你是如何进行SQL调优的?
点击右上角即可分享
微信分享提示