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 声明

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

  • 相关阅读:
    Vsftpd 3.0.2 正式版发布
    Putdb WebBuilder 6.5 正式版本发布
    SoaBox 1.1.6 GA 发布,SOA 模拟环境
    pynag 0.4.6 发布,Nagios配置和插件管理
    Percona Playback 0.4,MySQL 负荷回放工具
    xombrero 1.3.1 发布,微型 Web 浏览器
    Hypertable 0.9.6.4 发布,分布式数据库
    libmemcached 1.0.11 发布
    CryptoHeaven 3.7 发布,安全邮件解决方案
    Android Activity生命周期
  • 原文地址:https://www.cnblogs.com/4kapple/p/2308619.html
Copyright © 2011-2022 走看看