如何使用緩存提高程序性能


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

以上純為技術交流,對顧磊、黃聰等技術牛人十分敬佩,文中也引用了他們的文中和大量代碼,再次感謝。 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM