zoukankan      html  css  js  c++  java
  • 巧用lock解决缓存击穿的解决方案

    背景

    缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。

    解决方案

        1、设置热点数据永远不过期。

        2、加互斥锁,互斥锁参考代码如下:

             2.1、根据key生成object()

    private static object GetMemoryCacheLockObject(string key)
            {
                string cacheLockKey = string.Format(MemoryCacheLockObjectFormat, key);
                lock (CacheObject)
                {
                    var lockObject = CacheObject[cacheLockKey];
                    if (lockObject == null)
                    {
                        // 取得每個 Key專屬的 lock object;若同時有多個 thread要求相同資料,只會(到資料庫)查第一次,剩下的從 cache讀取
                        lockObject = new object();
                        CacheObject.Set(
                            cacheLockKey,
                            lockObject,
                            new System.Runtime.Caching.CacheItemPolicy()
                            {
                                AbsoluteExpiration = DateTimeOffset.UtcNow.AddMinutes(10)
                            }
                        );
                    }
    
                    return lockObject;
                }
            }

    2.2、lock住GetMemoryCacheLockObject(key)

     public T Get<T>(string key, Func<T> getDataWork, TimeSpan absoluteExpireTime, bool forceRefresh = false, bool returnCopy = true) where T : class
            {
                try
                {
                    lock (GetMemoryCacheLockObject(key))
                    {
                        /*
    System.ArgumentNullException: Value cannot be null.
    at System.Threading.Monitor.Enter(Object obj)
    at BQoolCommon.Helpers.Cache.MemoryCacheLayer.Get[T](String key, Func`1 getDataWork, TimeSpan absoluteExpireTime, Boolean forceRefresh, Boolean returnCopy) in D:SourceBQoolCommonBQoolCommon.HelpersCacheMemoryCacheLayer.cs:line 46
                         */
                        T result = CacheObject[key] as T;
    
                        if (result != null && forceRefresh)
                        {// 是否清除Cache,強制重查
                            result = null;
                        }
    
                        if (result == null)
                        {
                            //執行取得資料的委派作業
                            result = getDataWork();
    
                            if (result != null)
                            {
                                Set(key, result, absoluteExpireTime);
                            }
                        }
    
                        if (returnCopy)
                        {
                            //複製一份新的參考
                            string serialize = JsonConvert.SerializeObject(result);
                            return JsonConvert.DeserializeObject<T>(serialize);
                        }
                        else
                        {
                            return result;
                        }
                    }
                }
                catch
                {
                    return getDataWork();
                }
            }

    总结说明

    1、缓存中有数据,直接走下述代码就返回结果了

      T result = CacheObject[key] as T;

      2、缓存中没有数据,第1个进入的线程,获取锁并从数据库去取数据,没释放锁之前,其他并行进入的线程会等待,再重新去缓存取数据。这样就防止都去数据库重复取数据,重复往缓存中更新数据情况出现。

     try
                {
                    lock (GetMemoryCacheLockObject(key))
                    {
                        /*
    System.ArgumentNullException: Value cannot be null.
    at System.Threading.Monitor.Enter(Object obj)
    at BQoolCommon.Helpers.Cache.MemoryCacheLayer.Get[T](String key, Func`1 getDataWork, TimeSpan absoluteExpireTime, Boolean forceRefresh, Boolean returnCopy) in D:SourceBQoolCommonBQoolCommon.HelpersCacheMemoryCacheLayer.cs:line 46
                         */
                        T result = CacheObject[key] as T;

    3、取得每个 Key专有的 lock object;若同时有多个 thread要求相同资料,只会(到数据库)查第一次,剩下的从 cache读取。

     string cacheLockKey = string.Format(MemoryCacheLockObjectFormat, key);
                lock (CacheObject)
                {
                    var lockObject = CacheObject[cacheLockKey];
                    if (lockObject == null)
                    {
                        // 取得每個 Key專屬的 lock object;若同時有多個 thread要求相同資料,只會(到資料庫)查第一次,剩下的從 cache讀取
                        lockObject = new object();
  • 相关阅读:
    并查集
    强联通分量,缩点
    最短路径
    最小生成树
    拓扑排序
    图的遍历
    图论基础知识
    数据库四种隔离级别
    MySQL 索引 乐观锁 悲观锁
    MYSQL+正则
  • 原文地址:https://www.cnblogs.com/lyl6796910/p/14354503.html
Copyright © 2011-2022 走看看