欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C#實現(xiàn)自由組合本地緩存、分布式緩存和數(shù)據(jù)查詢

 更新時間:2022年07月21日 08:34:39   作者:奮斗的大橙子  
這篇文章介紹了C#實現(xiàn)本地緩存、分布式緩存和數(shù)據(jù)查詢的方法,文中通過示例代碼介紹的非常詳細。對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下

一、背景介紹:

我們在進行數(shù)據(jù)存儲的時候,有時候會加入本地緩存、分布式緩存以及數(shù)據(jù)庫存儲三級的結構,當我們取值的時候經常是像下面這樣的流程:

1.先取本地緩存,如果值存在直接返回

2.本地緩存不存在,獲取分布式緩存,存在直接返回,并更新本地緩存

3.分布式緩存不存在,查詢數(shù)據(jù)庫,更新分布式緩存、更新本地緩存,最后返回

但如果對于一些場景,可能只有本地緩存、只有分布式緩存或者說上面三種的幾種組合,我們怎么要應對這樣的變化,怎么能抽象出一套方式,能夠應對各種不同數(shù)據(jù)存儲方式造成的變化。

二、設計思路:

首先我們分析一下上面這個過程的模型,可以抽象出5個方法:

  • 1.GetDataFromLocalCache
  • 2.GetDataFromDistributeCache
  • 3.GetDataFromDB
  • 4.SetDataToLocalCache
  • 5.SetDataToDistributeCache

其實,不同的場景無非就是這幾個方法的組合,只不過里面的內容不同罷了,說到這里我們應該已經有思路了,可以利用委托來實現(xiàn)。

三、詳細設計:

①定義一個類,包含上面五個方法的委托;

public class DataOperateInput<T>
{
    public Func<T> GetDataFromLocalCache { get; set; } = null;        //獲取本地緩存數(shù)據(jù)
    public Func<T> GetDataFromDistributeCache { get; set; } = null;   //獲取分布式緩存數(shù)據(jù)
    public Func<T> GetDataFromDb { get; set; } = null;                //獲取DB數(shù)據(jù)
    public Action<T> SetDataTolocalCache { get; set; } = null;        //設置本地緩存數(shù)據(jù)
    public Action<T> SetDataToDistributeCache { get; set; } = null;   //設置分布式緩存數(shù)據(jù)
}

②實現(xiàn)一個方法,組合這五個方法。

    public class DataOperate
    {
        /// <summary>
        /// 獲取數(shù)據(jù)入口
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="input"></param>
        /// <returns></returns>
        public T GetData<T>(DataOperateInput<T> input) where T : class, new()
        {
            T result = null;

            //需要從本地緩存取
            if (input.GetDataFromLocalCache != null)
            {
                //調用本地緩存委托方法獲取值
                result = input.GetDataFromLocalCache();

                if (result != null)
                {
                    return result;
                }
            }
            //獲取值為空或者不從本地緩存獲取,調用下面的方法,從分布式緩存和Db中獲取數(shù)據(jù)
            return GetDataFromDistributeAndDB(input);
        }

        /// <summary>
        /// 從分布式緩存和Db中獲取數(shù)據(jù)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="input"></param>
        /// <returns></returns>
        private T GetDataFromDistributeAndDB<T>(DataOperateInput<T> input) where T : class, new()
        {
            T result = null;

            if (input.GetDataFromDistributeCache != null)
            {
                //從緩存中取值
                result = input.GetDataFromDistributeCache();

                //如果需要設置會本地緩存,那么設置
                if (result != null)
                {
                    //如果設置本地緩存的委托存在,調用它設置本地緩存
                    input.SetDataTolocalCache?.Invoke(result);
                }
            }

            //獲取值為空或者不從分布式緩存獲取,調用下面的方法,從Db中獲取數(shù)據(jù)
            return GetDataFromDB(input);
        }

        /// <summary>
        /// 從數(shù)據(jù)庫中獲取數(shù)據(jù)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="input"></param>
        /// <returns></returns>
        private T GetDataFromDB<T>(DataOperateInput<T> input) where T : class, new()
        {
            T result = null;
            if (input.GetDataFromDb != null)
            {
                //從DB中取值
                result = input.GetDataFromDb();

                //如果需要設置會分布式緩存和本地緩存,那么設置
                if (result != null)
                {
                    input.SetDataToDistributeCache?.Invoke(result);
                    input.SetDataTolocalCache?.Invoke(result);
                }
            }
            return result;
        }

    }

③ 具體實現(xiàn)一個服務類,和各種GetData、SetData方法;

A.定義一個枚舉類,通過這個枚舉可以自由組合數(shù)據(jù)源

/// <summary>
/// 數(shù)據(jù)源類別
/// </summary>
[Flags]
public enum DataSourceKind
{
    /// <summary>
    /// 本地緩存
    /// </summary>
    LocalCache = 1,
    /// <summary>
    /// 分布式緩存
    /// </summary>
    DistributeCache = 2,
    /// <summary>
    /// 數(shù)據(jù)庫
    /// </summary>
    DataBase = 4
}

B.定義一個具體的實體類,舉例我這里定義了一個User類

public class User : IUser
{
    public long UserId { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public int Sex { get; set; }
}

C.實現(xiàn)一個獲取用戶信息的方法

    /// <summary>
    /// 獲取用戶數(shù)據(jù)
    /// </summary>
    /// <param name="userId">用戶Id(可以是自己相關的業(yè)務代碼)</param>
    /// <param name="dataSources">數(shù)據(jù)源類型(調用方可以自己組合)</param>
    /// <param name="needUpdatelocal">是否需要更新本地緩存</param>
    /// <param name="needUpdateDistribute">是否需要更新分布式緩存</param>
    /// <returns></returns>
    public User GetUserInfo(long userId, 
        DataSourceKind dataSources = DataSourceKind.LocalCache , 
        bool needUpdatelocal = false, 
        bool needUpdateDistribute = false)
    {
        Console.WriteLine($"======數(shù)據(jù)源:{dataSources.ToString()} 是否更新本地:{needUpdatelocal}  是否更新Redis:{needUpdateDistribute}======");
   
        //初始化一個輸入?yún)?shù)類
        var input = new DataOperateInput<User>();

        //如果包含從本地緩存取值
        if (dataSources.HasFlag(DataSourceKind.LocalCache))
        {
            input.GetDataFromLocalCache = () =>
            {
                //?。∵@里可以寫具體的 獲取本地緩存的處理邏輯
                return GetUserFromLocalCache(userId);
            };
        }

        //如果包含從分布式緩存取值
        if (dataSources.HasFlag(DataSourceKind.DistributeCache))
        {
            input.GetDataFromDistributeCache = () =>
            {       
                //?。∵@里可以寫具體的獲取分布式緩存的處理邏輯
                return GetUserFromRedisCache(userId);
            };

            if (needUpdatelocal)
            {
                input.SetDataTolocalCache = (value) =>
                {
                    //??!這里可以寫具體的設定本地緩存的處理邏輯
                    SetUserToLocalCache(value);
                };
            }
        }

        //如果包含從數(shù)據(jù)庫緩存取值
        if (dataSources.HasFlag(DataSourceKind.DataBase))
        {
            input.GetDataFromDb = () =>
            {
                //??!這里可以寫具體的獲取數(shù)據(jù)庫數(shù)據(jù)的處理邏輯
                return GetUserFromDB(userId);
            };

            if (needUpdateDistribute)
            {
                //!!這里可以寫具體的設定分布式緩存的處理邏輯
                input.SetDataToDistributeCache = (value) =>
                {
                    SetUserToRedisCache(value);
                };
            }

            if (needUpdatelocal)
            {
                //!!這里可以寫具體的設定本地緩存的處理邏輯
                input.SetDataTolocalCache = (value) =>
                {
                    SetUserToLocalCache(value);
                };
            }
        }

        //執(zhí)行我們組合好的input
        var result =  new DataOperate().GetData(input);

        Console.WriteLine("=============================================\n");

        return result;
    }

上面的代碼描述了使用封裝好的GetData的方法的使用,其中有些委托的方法是需要具體實現(xiàn)的,這里我沒有詳細寫。下面列出用于測試的GetUserFromLocalCache、GetUserFromRedisCache、GetUserFromDB、SetUserToLocalCache以及SetUserToRedisCache的代碼。

/// <summary>
/// 從本地緩存獲取用戶信息
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
private User GetUserFromLocalCache(long userId)
{
    User user = null;

    if (userId == 1 )
    {
        user = new User
        {
            UserId = userId,
            Age = 10,
            Name = $"BigOrange_{userId}",
            Sex = 1
        };
    }

    if (user == null)
    {
        Console.WriteLine($"從本地緩存取值 未查詢到  UserId={userId}");
    }
    else
    {
        Console.WriteLine($"從本地緩存取值  UserId={user.UserId} Name={user.Name} ");
    }
    
    return user;
}

/// <summary>
/// 從Redis緩存獲取用戶信息
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
private User GetUserFromRedisCache(long userId )
{
    User user = null;
    
    if (userId == 1 || userId == 2 )
    {
        user =  new User
        {
            UserId = userId,
            Age = 10,
            Name = $"BigOrange_{userId}",
            Sex = 1
        };
    }

    if (user == null)
    {
        Console.WriteLine($"從Redis緩存取值 未查詢到  UserId={userId}");
    }
    else
    {
        Console.WriteLine($"從Redis緩存取值 UserId={user.UserId} Name={user.Name}");
    }

    return user;
}

/// <summary>
/// 從DB獲取用戶信息
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
private User GetUserFromDB(long userId)
{
    Console.WriteLine("從數(shù)據(jù)庫中取值");

    User user = null;

    if (userId == 1 || userId == 2 || userId == 3)
    {
        user = new User
        {
            UserId = userId,
            Age = 10,
            Name = $"BigOrange_{userId}",
            Sex = 1
        };
    }

    if (user == null)
    {
        Console.WriteLine($"從DB取值 未查詢到  UserId={userId}");
    }
    else
    {
        Console.WriteLine($"從DB取值 UserId={user.UserId} Name={user.Name}");
    }


    return user;
}

/// <summary>
/// 設置用戶信息到本地緩存
/// </summary>
/// <param name="userInfo"></param>
/// <returns></returns>
private bool SetUserToLocalCache(User userInfo)
{
    Console.WriteLine($"設置值到本地緩存:useId = {userInfo.UserId}");
    return true;
}

/// <summary>
/// 設置用戶信息到Redis緩存
/// </summary>
/// <param name="userInfo"></param>
/// <returns></returns>
private bool SetUserToRedisCache(User userInfo)
{
    Console.WriteLine($"設置值到Redis緩存:useId = {userInfo.UserId}");
    return true;
}

④測試一下

根據(jù)上面的代碼,寫了一些測試用的條目:

static void Main(string[] args)
{
    var userInfoService = new UserInfoService();

    /*
     * 測試用例 
       數(shù)據(jù)庫中存在 User1、User2、User3 
       分布式緩存 User1、User2
       本地緩存 User1
     */

    //1.只從本地緩存取值
    userInfoService.GetUserInfo(1, DataSourceKind.LocalCache);  
    userInfoService.GetUserInfo(2, DataSourceKind.LocalCache); 

    //2.只從Redis緩存取值
    userInfoService.GetUserInfo(2, DataSourceKind.DistributeCache);  
    userInfoService.GetUserInfo(3, DataSourceKind.DistributeCache);  

    //3.只從DB取值
    userInfoService.GetUserInfo(3, DataSourceKind.DataBase); 
    userInfoService.GetUserInfo(4, DataSourceKind.DataBase); 

    //4.從本地緩存和Redis取值
    userInfoService.GetUserInfo(1, DataSourceKind.LocalCache | DataSourceKind.DistributeCache);
    //不更新到本地
    userInfoService.GetUserInfo(2, DataSourceKind.LocalCache | DataSourceKind.DistributeCache, false);
    //更新到本地
    userInfoService.GetUserInfo(2, DataSourceKind.LocalCache | DataSourceKind.DistributeCache, true);

    //5.從Redis和DB取值
    userInfoService.GetUserInfo(2, DataSourceKind.DistributeCache | DataSourceKind.DataBase);
    userInfoService.GetUserInfo(3, DataSourceKind.DistributeCache | DataSourceKind.DataBase, false, false);
    userInfoService.GetUserInfo(3, DataSourceKind.DistributeCache | DataSourceKind.DataBase, false, true);

    //6.從本地和DB取值
    userInfoService.GetUserInfo(1, DataSourceKind.LocalCache | DataSourceKind.DataBase);
    userInfoService.GetUserInfo(3, DataSourceKind.LocalCache | DataSourceKind.DataBase, false,false);
    userInfoService.GetUserInfo(3, DataSourceKind.LocalCache | DataSourceKind.DataBase, true, false);

    //7.三者都使用
    userInfoService.GetUserInfo(1, DataSourceKind.LocalCache | DataSourceKind.DistributeCache | DataSourceKind.DataBase,false,false);
    userInfoService.GetUserInfo(2, DataSourceKind.LocalCache | DataSourceKind.DistributeCache | DataSourceKind.DataBase,false,false);
    userInfoService.GetUserInfo(2, DataSourceKind.LocalCache | DataSourceKind.DistributeCache | DataSourceKind.DataBase, true,false);
    userInfoService.GetUserInfo(3, DataSourceKind.LocalCache | DataSourceKind.DistributeCache | DataSourceKind.DataBase,false,false);
    userInfoService.GetUserInfo(3, DataSourceKind.LocalCache | DataSourceKind.DistributeCache | DataSourceKind.DataBase, true, false);
    userInfoService.GetUserInfo(3, DataSourceKind.LocalCache | DataSourceKind.DistributeCache | DataSourceKind.DataBase, false, true);
    userInfoService.GetUserInfo(3, DataSourceKind.LocalCache | DataSourceKind.DistributeCache | DataSourceKind.DataBase, true,true);

    Console.ReadKey();
}

執(zhí)行結果:

======數(shù)據(jù)源:LocalCache 是否更新本地:False 是否更新Redis:False======
從本地緩存取值 UserId=1 Name=BigOrange_1
=============================================

======數(shù)據(jù)源:LocalCache 是否更新本地:False 是否更新Redis:False======
從本地緩存取值 未查詢到 UserId=2
=============================================

======數(shù)據(jù)源:DistributeCache 是否更新本地:False 是否更新Redis:False======
從Redis緩存取值 UserId=2 Name=BigOrange_2
=============================================

======數(shù)據(jù)源:DistributeCache 是否更新本地:False 是否更新Redis:False======
從Redis緩存取值 未查詢到 UserId=3
=============================================

======數(shù)據(jù)源:DataBase 是否更新本地:False 是否更新Redis:False======
從DB取值 UserId=3 Name=BigOrange_3
=============================================

======數(shù)據(jù)源:DataBase 是否更新本地:False 是否更新Redis:False======
從DB取值 未查詢到 UserId=4
=============================================

======數(shù)據(jù)源:LocalCache, DistributeCache 是否更新本地:False 是否更新Redis:False======
從本地緩存取值 UserId=1 Name=BigOrange_1
=============================================

======數(shù)據(jù)源:LocalCache, DistributeCache 是否更新本地:False 是否更新Redis:False======
從本地緩存取值 未查詢到 UserId=2
從Redis緩存取值 UserId=2 Name=BigOrange_2
=============================================

======數(shù)據(jù)源:LocalCache, DistributeCache 是否更新本地:True 是否更新Redis:False======
從本地緩存取值 未查詢到 UserId=2
從Redis緩存取值 UserId=2 Name=BigOrange_2
設置值到本地緩存:useId = 2
=============================================

======數(shù)據(jù)源:DistributeCache, DataBase 是否更新本地:False 是否更新Redis:False======
從Redis緩存取值 UserId=2 Name=BigOrange_2
從DB取值 UserId=2 Name=BigOrange_2
=============================================

======數(shù)據(jù)源:DistributeCache, DataBase 是否更新本地:False 是否更新Redis:False======
從Redis緩存取值 未查詢到 UserId=3
從DB取值 UserId=3 Name=BigOrange_3
=============================================

======數(shù)據(jù)源:DistributeCache, DataBase 是否更新本地:False 是否更新Redis:True======
從Redis緩存取值 未查詢到 UserId=3
從DB取值 UserId=3 Name=BigOrange_3
設置值到Redis緩存:useId = 3
=============================================

======數(shù)據(jù)源:LocalCache, DataBase 是否更新本地:False 是否更新Redis:False======
從本地緩存取值 UserId=1 Name=BigOrange_1
=============================================

======數(shù)據(jù)源:LocalCache, DataBase 是否更新本地:False 是否更新Redis:False======
從本地緩存取值 未查詢到 UserId=3
從DB取值 UserId=3 Name=BigOrange_3
=============================================

======數(shù)據(jù)源:LocalCache, DataBase 是否更新本地:True 是否更新Redis:False======
從本地緩存取值 未查詢到 UserId=3
從DB取值 UserId=3 Name=BigOrange_3
設置值到本地緩存:useId = 3
=============================================

======數(shù)據(jù)源:LocalCache, DistributeCache, DataBase 是否更新本地:False 是否更新Redis:False======
從本地緩存取值 UserId=1 Name=BigOrange_1
=============================================

======數(shù)據(jù)源:LocalCache, DistributeCache, DataBase 是否更新本地:False 是否更新Redis:False======
從本地緩存取值 未查詢到 UserId=2
從Redis緩存取值 UserId=2 Name=BigOrange_2
從DB取值 UserId=2 Name=BigOrange_2
=============================================

======數(shù)據(jù)源:LocalCache, DistributeCache, DataBase 是否更新本地:True 是否更新Redis:False======
從本地緩存取值 未查詢到 UserId=2
從Redis緩存取值 UserId=2 Name=BigOrange_2
設置值到本地緩存:useId = 2
從DB取值 UserId=2 Name=BigOrange_2
設置值到本地緩存:useId = 2
=============================================

======數(shù)據(jù)源:LocalCache, DistributeCache, DataBase 是否更新本地:False 是否更新Redis:False======
從本地緩存取值 未查詢到 UserId=3
從Redis緩存取值 未查詢到 UserId=3
從DB取值 UserId=3 Name=BigOrange_3
=============================================

======數(shù)據(jù)源:LocalCache, DistributeCache, DataBase 是否更新本地:True 是否更新Redis:False======
從本地緩存取值 未查詢到 UserId=3
從Redis緩存取值 未查詢到 UserId=3
從DB取值 UserId=3 Name=BigOrange_3
設置值到本地緩存:useId = 3
=============================================

======數(shù)據(jù)源:LocalCache, DistributeCache, DataBase 是否更新本地:False 是否更新Redis:True======
從本地緩存取值 未查詢到 UserId=3
從Redis緩存取值 未查詢到 UserId=3
從DB取值 UserId=3 Name=BigOrange_3
設置值到Redis緩存:useId = 3
=============================================

======數(shù)據(jù)源:LocalCache, DistributeCache, DataBase 是否更新本地:True 是否更新Redis:True======
從本地緩存取值 未查詢到 UserId=3
從Redis緩存取值 未查詢到 UserId=3
從DB取值 UserId=3 Name=BigOrange_3
設置值到Redis緩存:useId = 3
設置值到本地緩存:useId = 3
=============================================

四、總結一下

類似上面的用戶信息,可能對于不同系統(tǒng)、不同性能要求,獲取方式會有所不同。

打個比方:對于一個后臺管理系統(tǒng),用戶信息獲取是一個低頻操作,可能只需要從數(shù)據(jù)庫中獲取,此時一般后臺系統(tǒng)不會設置本地緩存和分布式緩存,而對于一個接口系統(tǒng),可能每天有幾百萬的訪問量,此時如果只從數(shù)據(jù)庫獲取,很難承受,所以要利用到分布式緩存和本地緩存。層次越多那么變化和組合也就越多,但是每個實體的存取如果都各自實現(xiàn)自己的方式,又比較浪費,所以如果能抽象出一套方法,只需要告訴方法存取的方式,然后得到自己想要的數(shù)據(jù),或許這樣是比較好的方式,而具體怎么拿、怎么存,還是由調用的人去給出,這樣可以應對復雜的規(guī)則。這也是為什么要使用這么多委托的原因,由于像上面獲取和設定User緩存的方式多種多樣,這么做可以把具體的獲取和設置緩存的操作開放給使用者。在系統(tǒng)重構方面上,可以將一些通用的方法抽象出來,相對成本較低,擴展性好一些。

五、題外話

上面的代碼中對于更新數(shù)據(jù),沒有做線程安全處理,多個進程去更新分布式緩存、同一進程的多個線程去更新本地緩存,可能都需要進行鎖操作。

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,謝謝大家對腳本之家的支持。如果你想了解更多相關內容請查看下面相關鏈接

相關文章

  • C#表達式中的動態(tài)查詢詳解【譯】

    C#表達式中的動態(tài)查詢詳解【譯】

    這篇文章主要給大家介紹了關于C#表達式中動態(tài)查詢的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-01-01
  • C#簡單發(fā)送email的方法

    C#簡單發(fā)送email的方法

    這篇文章主要介紹了C#簡單發(fā)送email的方法,涉及C#發(fā)送Email的相關技巧,非常具有實用價值,需要的朋友可以參考下
    2015-04-04
  • C#簡單判斷字符編碼的方法

    C#簡單判斷字符編碼的方法

    這篇文章主要介紹了C#簡單判斷字符編碼的方法,可實現(xiàn)判斷utf-8,unicode,ansi等編碼的功能,簡單實用,需要的朋友可以參考下
    2016-06-06
  • C#實現(xiàn)餐廳管理系統(tǒng)

    C#實現(xiàn)餐廳管理系統(tǒng)

    這篇文章主要為大家詳細介紹了C#實現(xiàn)餐廳管理系統(tǒng),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-01-01
  • C#操作CSV文件類實例

    C#操作CSV文件類實例

    這篇文章主要介紹了C#操作CSV文件類,涉及C#針對csv文件的讀取、轉化等常用操作技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-03-03
  • C#隱式運行CMD命令(隱藏命令窗口)

    C#隱式運行CMD命令(隱藏命令窗口)

    這篇文章主要介紹了C#隱式運行CMD命令(隱藏命令窗口),本文實現(xiàn)在winform窗口中運行CMD命令,需要的朋友可以參考下
    2015-06-06
  • C#基于Windows服務的聊天程序(1)

    C#基于Windows服務的聊天程序(1)

    這篇文章主要為大家詳細介紹了C#基于Windows服務的聊天程序的第一篇,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-07-07
  • C# 計算傳入的時間距離今天的時間差

    C# 計算傳入的時間距離今天的時間差

    本文通過一段簡單的代碼給大家介紹了C# 計算傳入的時間距離今天的時間差,代碼簡單易懂,需要的朋友參考下吧
    2017-08-08
  • C# string轉換為幾種不同編碼的Byte[]的問題解讀

    C# string轉換為幾種不同編碼的Byte[]的問題解讀

    這篇文章主要介紹了C# string轉換為幾種不同編碼的Byte[]的問題解讀,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • Entity Framework主從表的增刪改

    Entity Framework主從表的增刪改

    這篇文章介紹了Entity Framework主從表的增刪改,文中通過示例代碼介紹的非常詳細。對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-06-06

最新評論