zoukankan      html  css  js  c++  java
  • 如何使用缓存提高程序性能

    1 写在前面

    此文主要参考了园子里以下两篇文章:

    黄聪,Microsoft Enterprise Library 5.0 系列() : Caching Application Block (初级

    顾磊,[EntLib]微软企业库5.0 学习之路——第四步、使用缓存提高网站的性能(EntLib Caching)  

    2 前面两篇博文写的很好,很全面,为何还需要本文?

    大家可以点进去看下前面的文章,黄聪写的是企业库Cache基本用法,顾磊的文章比较深入,而且自定义了CacheHelper类,实用性更强,我也抄袭了这个类(^_^ )。

    我写此文的目的主要是记录下如果在项目中引入操作Cache、缓存哪些内容及最后的单元测试等

    主要是在缓存哪些数据的问题上,可能和顾磊的文章有些不同。 

    3 项目中引入Cache

    首先从微软网站下载并安装Enterprise Library 5.0 我这里Cache主要用在DataAccess这个项目中,是一个类库项目,所以Config的东西先不用配置,直接添加Microsoft.Practices.EnterpriseLibrary.Caching.dll的引用,

    然后添加CacheHelper类,代码: 

    View Code 
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    using Microsoft.Practices.EnterpriseLibrary.Caching;
    using Microsoft.Practices.EnterpriseLibrary.Caching.Expirations;
    using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;

    using System.Reflection;

    namespace DataAccess
    {
        public static class CacheHelper
        {
            private static ICacheManager cache = CacheFactory.GetCacheManager();

            /// <summary>
            
    /// Add Cache
            
    /// </summary>
            
    /// <param name="key"></param>
            
    /// <param name="value"></param>
            
    /// <param name="isRefresh">if true, should reload data every 5 mins; default value = false</param>
            public static void Add(string key, object value, bool isRefresh)
            {
                if (isRefresh)
                    cache.Add(key, value, CacheItemPriority.Normal, new ATSCacheItemRefreshAction(), new AbsoluteTime(TimeSpan.FromMinutes(5)));
                else
                    cache.Add(key, value);
            }
            // Summary:
            
    //     Adds new CacheItem to cache. If another item already exists with the same
            
    //     key, that item is removed before the new item is added. If any failure occurs
            
    //     during this process, the cache will not contain the item being added. Items
            
    //     added with this method will be not expire, and will have a Normal Microsoft.Practices.EnterpriseLibrary.Caching.CacheItemPriority
            
    //     priority.
            
    //
            
    // Parameters:
            
    //   key:
            
    //     Identifier for this CacheItem
            
    //
            
    //   value:
            
    //     Value to be stored in cache. May be null.
            
    //
            
    // Exceptions:
            
    //   System.ArgumentNullException:
            
    //     Provided key is null
            
    //
            
    //   System.ArgumentException:
            
    //     Provided key is an empty string
            
    //
            
    // Remarks:
            
    //     The CacheManager can be configured to use different storage mechanisms in
            
    //     which to store the CacheItems.  Each of these storage mechanisms can throw
            
    //     exceptions particular to their own implementations.
            public static void Add(string key, object value)
            {
                Add(key, value, false);
            }

            public static object Get(string key)
            {
                return cache.GetData(key);
            }

            public static void Remove(string key)
            {
                cache.Remove(key);
            }
        }

        [Serializable]
        public class ATSCacheItemRefreshAction : ICacheItemRefreshAction
        {

            #region ICacheItemRefreshAction Members

            public void Refresh(string removedKey, object expiredValue, CacheItemRemovedReason removalReason)
            {
                //when expired, reload it.
                if (removalReason == CacheItemRemovedReason.Expired)
                {
                    ICacheManager c = CacheFactory.GetCacheManager();
                    c.Add(removedKey, expiredValue);
                }
            }

            #endregion
        }

    4 缓存数据

    在顾磊的文章中,他主要缓存一些Class,如ClassInfoServiceStudentService等,代码如下: 

    View Code 
    /// <summary>
            
    /// 通用对象反射(包含缓存)
            
    /// </summary>
            
    /// <param name="className">要反射的类名</param>
            
    /// <returns></returns>
            public static T CreateObject(string className)
            {
                var typeName = assemblyString + "." + className;
                //判断对象是否被缓存,如果已经缓存则直接从缓存中读取,反之则直接反射并缓存
                var obj = (T)CacheHelper.GetCache(typeName);
                if (obj == null)
                {
                    obj = (T)Assembly.Load(assemblyString).CreateInstance(typeName, true);
                    CacheHelper.Add(typeName, obj, true);
                }
                return obj;
            }
    public static IStudentService CreateStudent()
            {
                string typeName = assemblyString + ".StudentService";
                if (CacheHelper.GetCache(typeName) != null)
                {
                    return (IStudentService)CacheHelper.GetCache(typeName);
                }
                else
                {
                    IStudentService service = (IStudentService)Assembly.Load(assemblyString).CreateInstance(typeName, true);
                    CacheHelper.Add(typeName, service, true);
                    return service;
                }

    而像StudentService这种Class,如果New StudentService()这样一个实例的话,所占的CPU和内存都是很小的,我觉得更有必要的是缓存数据,从数据库中查询回来的数据。

    我们看下顾磊代码中如何操作数据的:

    class StudentManage 

    View Code 
    private static readonly IStudentService studentService = DataAccess<IStudentService>.CreateObject("StudentService");

    public Student SelectById(int id)
            {
                return studentService.SelectById(id);

    class StudentService 

    View Code 
            /// <summary>
            
    /// 根据学生ID查询学生对象
            
    /// </summary>
            
    /// <param name="id">学生ID</param>
            
    /// <returns></returns>
            public Student SelectById(int id)
            {
                Student student = null;
                Database db = DBHelper.CreateDataBase();
                StringBuilder sb = new StringBuilder();
                sb.Append("select * from Student ");
                sb.Append(" where ID=@ID");
                DbCommand cmd = db.GetSqlStringCommand(sb.ToString());
                db.AddInParameter(cmd, "@ID", DbType.Int32, id);
     
                using (IDataReader reader = db.ExecuteReader(cmd))
                {
                    if (reader.Read())
                    {
                        student = new Student()
                        {
                            Id = reader.GetInt32(0),
                            ClassId = reader.GetInt32(1),
                            Sid = reader.GetString(2),
                            Password = reader.GetString(3),
                            Name = reader.GetString(4),
                            Sex = reader.GetInt32(5),
                            Birthday = reader.GetDateTime(6),
                            IsAdmin = reader.GetInt32(7)
                        };
                    }
                }
     
                return student;

    大家从上面的代码可以看出,缓存中存放了StudentService这个类,但是在SelectByIdint id)这个函数中,并没有缓存任何东西,还是每次从数据库中查询数据,这样设计缓存,对程序性能的提升是十分有限的。

    5 我程序中如何缓存数据

    我还是用上面顾磊的代码吧,那个好理解。然后我将每次查询到的数据缓存起来,如果下次查询,先从缓存中取数据,有则自动返回数据;没有,则从数据库中查询,然后添加到缓存中,缓存有自动的数据过期机制,过期的数据会自动删除。 

    View Code 
    public Student SelectByIdWithCache(int id)
            {
                Student student = (Student)CacheHelper.GetCache(id.ToString());
                if (student == null)
                {
                    student = SelectById(id);
                    CacheHelper.Add(id.ToString(), student);
                }
                return student;

    这里可能上面的代码不是十分恰当,但是为了让大家更清楚我的意图,及上面的代码十分简洁、易懂。 

    主要是我项目程序中有这样的特情况,查询参数是一个TableParameters,里面有很多字段: 

    View Code 
    public class TableParameters : IParameters<tblTable>
        {
            public Guid? tableGuid { getset; }
            public string tableName { getset; }
            public string tableDesc { getset; }
            public Guid? tableTypeGuid { getset; }
            public Guid? schemeGuid { getset; }
            public Guid index1TypeGuid { getset; }
            public Guid? latestTableVersionGuid { getset; }
            public Guid? tableFormatGuid { getset; }
            public Guid? index2TypeGuid { getset; }

    在第一次会通过一个条件,查询得到一个List,之后会直接传输一个Guid过来,而这个guid往往会包含在上面的结果中,所以第一件会将List缓存,详细代码: 

    View Code 
    public List<tblTable> GetQueryList(IParameters<tblTable> t)
            {
                TableParameters param = (TableParameters)t;

                List<tblTable> query = new List<tblTable>();
                if (param.tableGuid != null)
                {
                    tblTable result = (tblTable)CacheHelper.Get(GuidTable + param.tableGuid.Value.ToString());
                    if (result != null)
                    {                  
                        query.Add(result);
                        return query;
                    }
                }

                var _tableQuery = from c in db.tblTable.Expand("TableType").Expand("TableFormat").Expand("Scheme").Expand("Index1Type").Expand("Index2Type").Expand("LatestTableVersionGUID")
                                                   select c;
                if (param.tableGuid != null)
                {
                    _tableQuery = _tableQuery.Where(n => n.TableGUID == param.tableGuid.Value);
                }
                if (!string.IsNullOrEmpty(param.tableName))
                {
                    _tableQuery = _tableQuery.Where(n => n.TableName.Contains(param.tableName));
                }
                if (param.tableTypeGuid != null)
                {
                    _tableQuery = _tableQuery.Where(n => n.TableType.TableTypeGUID == param.tableTypeGuid.Value);
                }
                if (param.tableFormatGuid != null)
                {
                    _tableQuery = _tableQuery.Where(n => n.TableFormat.TableFormatGUID == param.tableFormatGuid.Value);
                }
                if (param.schemeGuid != null)
                    _tableQuery = _tableQuery.Where(n => n.Scheme.SchemeGUID == param.schemeGuid.Value);
                
                query = _tableQuery.ToList<tblTable>();
                foreach (var tb in query)
                {
                    CacheHelper.Add(GuidTable + tb.TableGUID.ToString(), tb);
                }
                return query;

    6 单元测试

    这个单元测试不是测试顾磊代码的,是我项目中测试TableManager的,因为顾磊的代码更简明,所以上面我还是贴出了他的代码,至于单元测试,还是随便贴个我项目的,因为顾磊那个我没数据,没法写,呵呵~  

    View Code 
            /// <summary>
            
    ///A test for GetQueryList
            
    ///</summary>
            [TestMethod()]
            public void GetQueryListTest()
            {
               
                TableParameters t = new TableParameters();
                t.schemeGuid = new Guid("9C962C55-A598-40B8-A39B-11788161A9D8");
               
                List<tblTable> actual;
                actual = ManagerFactory.TableManager.GetQueryList(t);
                Assert.AreEqual(" Sedgwick Marsh- B & pre2000-NRA65", actual[0].TableName);
                //Assert.Inconclusive("Verify the correctness of this test method.");

    7 总结

    我认为缓存数据不仅仅要缓存Class类,更要缓存从数据库中查询过来的数据,这样才能最大限度的提示程序性能。

    8 声明

    以上纯为技术交流,对顾磊、黄聪等技术牛人十分敬佩,文中也引用了他们的文中和大量代码,再次感谢。 

  • 相关阅读:
    微信小程序 iPhone 11、iPhoneX 底部安全区域(底部小黑条)适配
    Servlet与vue-axios交互跨域问题之Access-Control-Allow-Origin' header contains multiple values '*, null', but only one is allowed.
    Windows10下PCL1.8.1以及Python-pcl1.81环境配置的掉发之路
    【问题记录】Navicat Premium连接mysql-8.0.17时出现2059
    mysql-8.0.17解压版安装步骤及MySQL服务无法启动问题的解决办法
    ENVI基本操作之彩色合成
    GeoServer与Udig发布矢量数据出现的问题1——预览数据一半显示正常一半重叠
    本地日志文件
    SQL语句(2)--- 函数
    SQL语句(1)--- SQL简介
  • 原文地址:https://www.cnblogs.com/4kapple/p/2308619.html
Copyright © 2011-2022 走看看