cache 訪問頻率的思考

互聯網的項目用戶基數很大,有時候瞬間併發量很是大,這個時候對於數據訪問來講是個災難。爲了應對這種場景,通常都會大量採用web服務器集羣,緩存集羣。採用集羣后基本上就能解決大量併發的數據訪問。固然這個時候內網的網速會成爲緩存速度的瓶頸。web

固然咱們但願能有更好的緩存結構,好比一級緩存和二級緩存。一級緩存直接緩存在宿主主機內存上,二級緩存緩存在redis集羣上,若是一個緩存實例被訪問的頻率很是高,咱們但願這個緩存實例能緩存在宿主主機內存上,若是一個實例的訪問頻率很是低,咱們甚至可能不會爲此實例進行緩存處理。redis

基於這種設想,咱們但願可以跟蹤監視緩存實例,並根據監視結果,對實例的緩存級別進行動態調整,以達到最佳的緩存效果。(事實上dotNet4.0裏面的System.Runtime.Caching.MemoryCache對此已經有很好的實現和支持了。固然咱們的應用必須知道要緩存在宿主主機內存上,仍是redis集羣上,那就必須實現相似System.Runtime.Caching.MemoryCache的監視功能和動態調整功能)緩存

首先咱們須要附加一些監視信息到緩存實例上,服務器

 public class CacheAttach
    {
        public CacheAttach(string key)
        {
            this.Key = key;
            this.InsertedTime = DateTime.Now;
        }
        public string Key { get; set; }
        public DateTime InsertedTime { get; private set; }
        public int QueryTimes { get; set; }
        public int AccessTimes { get; set; }
        public override bool Equals(object obj)
        {
            if (obj == null)
                return false;
            return obj.GetHashCode() == this.GetHashCode();
        }
        public override int GetHashCode()
        {
            return Key.GetHashCode();
        }
        public static implicit operator CacheAttach(string value)
        {
            return new CacheAttach(value);
        }
    }
    public class CacheAttachCollection : List<CacheAttach>, ICollection<CacheAttach>
    {
        public bool Contains(string Key)
        {
            return this.Find(i => i.Key == Key) == null;
        }
        public CacheAttach this[string key]
        {
            get
            {
                CacheAttach item =this.Find(i => i.Key == key);
                if (item == null)
                {
                    item = new CacheAttach(key);
                    this.Add(item);
                }
                return item;
            }
            set
            {
                CacheAttach item = this.Find(i => i.Key == key);
                if (item == null)
                {
                    item = new CacheAttach(key);
                    this.Add(item);
                }
                item = value;
            }
        }
    }

  這裏採用的是一種附加形式的監視,不去破壞原來的K/V緩存方式。這個時候咱們可能須要從新包裝一下原有的緩存訪問,使得對緩存的操做能被監視。併發

public class MonitorCache: ICache
    {
        private ICache proxyCache;
        CacheAttachCollection cacheMonitor = new CacheAttachCollection();
        public MonitorCache(ICache cache)
        {
            this.proxyCache = cache;
        }
        #region ICache Implement
        public bool Set<T>(string key, T value)
        {
            cacheMonitor[key].QueryTimes++;
            cacheMonitor[key].AccessTimes++;
            return proxyCache.Set(key, value);
        }

        public bool Set<T>(string key, T value, DateTime absoluteTime, TimeSpan slidingTime, Action<string, T> removingHandler)
        {
            cacheMonitor[key].QueryTimes++;
            cacheMonitor[key].AccessTimes++;
            return this.proxyCache.Set(key, value, absoluteTime, slidingTime, removingHandler);
        }

        public object Get(string key)
        {
            cacheMonitor[key].QueryTimes++;
            cacheMonitor[key].AccessTimes++;
            return this.proxyCache.Get(key);
        }

        public T Get<T>(string key)
        {
            cacheMonitor[key].QueryTimes++;
            cacheMonitor[key].AccessTimes++;
            return this.proxyCache.Get<T>(key);
        }

        public bool Contains(string key)
        {
            cacheMonitor[key].QueryTimes++;
            return this.proxyCache.Contains(key);
        }

        public bool Remove(string key)
        {
            if (this.proxyCache.Remove(key))
            {
                cacheMonitor.Remove(key);
                return true;
            }
            return false;
        }
        #endregion

        public object this[string key]
        {
            get
            {
                return this.Get(key);
            }
            set
            {
                this.Set(key, value);
            }
        }

        public CacheAttachCollection Monitor
        {
            get
            {
                return this.cacheMonitor;
            }
        }

    }

  經過對原有的緩存訪問進行包裝,咱們已經實現對原有緩存的重構,實現監視的意圖。ide

 public class CacheHelper : ICache
    {
        private MonitorCache level1 = null;
        private MonitorCache level2 = null;

        private CacheHelper()
        {
            this.level1 = new MonitorCache(new MemoryCache());
            this.level2 = new MonitorCache(new RedisCache());
        }

        public bool Set<T>(string key, T value)
        {
            if (this.level1.Set(key, value))
                return true;
            if (this.level2.Set(key, value))
                return true;
            return false;
        }

        public bool Set<T>(string key, T value, DateTime absoluteTime, TimeSpan slidingTime, Action<string, T> removingHandler)
        {
            if (this.level1.Set(key, value, absoluteTime, slidingTime, removingHandler))
                return true;
            if (this.level2.Set(key, value, absoluteTime, slidingTime, removingHandler))
                return true;
            return false;
        }

        public object Get(string key)
        {
            return this.level1.Get(key) ?? this.level2.Get(key) ?? null;
        }

        public T Get<T>(string key)
        {
            if (this.level1.Contains(key))
                return this.level1.Get<T>(key);
            if (this.level2.Contains(key))
                return this.level2.Get<T>(key);
            return default(T);
        }

        public T Get<T>(string key, Func<T> valueGetter)
        {
            var result = default(T);
            if (this.level1.Contains(key))
                result = this.level1.Get<T>(key);
            else if (this.level2.Contains(key))
                result = this.level2.Get<T>(key);

            if (result == null && valueGetter != null)
                result = valueGetter();
            return result;
        }

        public bool Contains(string key)
        {
            if (this.level1.Contains(key))
                return true;
            if (this.level2.Contains(key))
                return true;
            return false;
        }

        public bool Remove(string key)
        {
            if (this.level1.Contains(key))
                this.level1.Remove(key);
            if (this.level2.Contains(key))
                this.level2.Remove(key);
            return true;
        }

        public object this[string key]
        {
            get
            {
                return this.Get(key);
            }
            set
            {
                this.Set(key, value);
            }
        }
        public void Trim()
        {
            //對一級緩存進行整理
            for (int i = 0, lengh = this.level1.KeyMonitor.Count; i < lengh; i++)
            {
                CacheAttach item = this.level1.KeyMonitor[i];

                //頻率小於10次/秒的緩存須要移除一級緩存
                if (item.AccessRate < 10)
                {
                    //頻率大於1次/秒的緩存移到二級緩存
                    if (item.AccessRate >= 1)
                    {
                        this.level2.Set(item.Key, this.level1[item.Key]);
                        this.level2.KeyMonitor[item.Key] = item;
                    }
                    this.level1.Remove(item.Key);
                }
            }

            //對二級緩存進行整理
            for (int i = 0, lengh = this.level2.KeyMonitor.Count; i < lengh; i++)
            {
                CacheAttach item = this.level1.KeyMonitor[i];

                //頻率大於等於10次/秒的緩存須要移至一級緩存
                if (item.AccessRate >= 10)
                {
                    this.level1.Set(item.Key, this.level2[item.Key]);
                    this.level1.KeyMonitor[item.Key] = item;
                    this.level1.Remove(item.Key);
                    continue;
                }
                if (item.AccessRate < 1)
                {
                    this.level2.Remove(item.Key);
                    continue;
                }
            }
        }

        private static CacheHelper _Current = new CacheHelper();
        public static CacheHelper Current
        {
            get { return _Current; }
        }
        public static CacheHelper()
        {
            System.Threading.Timer timer = new System.Threading.Timer(delegate
            {
                Current.Trim();
            });
        }
    }
相關文章
相關標籤/搜索