麒麟v10 上部署 TiDB v5.1.2 生产环境优化实践
678
2023-04-06
C#自由组合本地缓存、分布式缓存和数据库的数据
一、背景介绍:
我们在进行数据存储的时候,有时候会加入本地缓存、分布式缓存以及数据库存储三级的结构,当我们取值的时候经常是像下面这样的流程:
1.先取本地缓存,如果值存在直接返回
2.本地缓存不存在,获取分布式缓存,存在直接返回,并更新本地缓存
3.分布式缓存不存在,查询数据库,更新分布式缓存、更新本地缓存,最后返回
但如果对于一些场景,可能只有本地缓存、只有分布式缓存或者说上面三种的几种组合,我们怎么要应对这样的变化,怎么能抽象出一套方式,能够应对各种不同数据存储方式造成的变化。
二、设计思路:
首先我们分析一下上面这个过程的模型,可以抽象出5个方法:
1.GetDataFromLocalCache
2.GetDataFromDistributeCache
3.GetDataFromDB
4.SetDataToLocalCache
5.SetDataToDistributeCache
其实,不同的场景无非就是这几个方法的组合,只不过里面的内容不同罢了,说到这里我们应该已经有思路了,可以利用委托来实现。
三、详细设计:
①定义一个类,包含上面五个方法的委托;
public class DataOperateInput
②实现一个方法,组合这五个方法。
public class DataOperate { ///
③ 具体实现一个服务类,和各种GetData、SetData方法;
A.定义一个枚举类,通过这个枚举可以自由组合数据源
///
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.实现一个获取用户信息的方法
///
上面的代码描述了使用封装好的GetData的方法的使用,其中有些委托的方法是需要具体实现的,这里我没有详细写。下面列出用于测试的GetUserFromLocalCache、GetUserFromRedisCache、GetUserFromDB、SetUserToLocalCache以及SetUserToRedisCache的代码。
///
④测试一下
根据上面的代码,写了一些测试用的条目:
static void Main(string[] args){ var userInfoService = new UserInfoService(); /* * 测试用例 数据库中存在 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();}
执行结果:
======数据源:LocalCache 是否更新本地:False 是否更新Redis:False======从本地缓存取值 UserId=1 Name=BigOrange_1===================================================数据源:LocalCache 是否更新本地:False 是否更新Redis:False======从本地缓存取值 未查询到 UserId=2===================================================数据源:DistributeCache 是否更新本地:False 是否更新Redis:False======从Redis缓存取值 UserId=2 Name=BigOrange_2===================================================数据源:DistributeCache 是否更新本地:False 是否更新Redis:False======从Redis缓存取值 未查询到 UserId=3===================================================数据源:DataBase 是否更新本地:False 是否更新Redis:False======从DB取值 UserId=3 Name=BigOrange_3===================================================数据源:DataBase 是否更新本地:False 是否更新Redis:False======从DB取值 未查询到 UserId=4===================================================数据源:LocalCache, DistributeCache 是否更新本地:False 是否更新Redis:False======从本地缓存取值 UserId=1 Name=BigOrange_1===================================================数据源:LocalCache, DistributeCache 是否更新本地:False 是否更新Redis:False======从本地缓存取值 未查询到 UserId=2从Redis缓存取值 UserId=2 Name=BigOrange_2===================================================数据源:LocalCache, DistributeCache 是否更新本地:True 是否更新Redis:False======从本地缓存取值 未查询到 UserId=2从Redis缓存取值 UserId=2 Name=BigOrange_2设置值到本地缓存:useId = 2===================================================数据源:DistributeCache, DataBase 是否更新本地:False 是否更新Redis:False======从Redis缓存取值 UserId=2 Name=BigOrange_2从DB取值 UserId=2 Name=BigOrange_2===================================================数据源:DistributeCache, DataBase 是否更新本地:False 是否更新Redis:False======从Redis缓存取值 未查询到 UserId=3从DB取值 UserId=3 Name=BigOrange_3===================================================数据源:DistributeCache, DataBase 是否更新本地:False 是否更新Redis:True======从Redis缓存取值 未查询到 UserId=3从DB取值 UserId=3 Name=BigOrange_3设置值到Redis缓存:useId = 3===================================================数据源:LocalCache, DataBase 是否更新本地:False 是否更新Redis:False======从本地缓存取值 UserId=1 Name=BigOrange_1===================================================数据源:LocalCache, DataBase 是否更新本地:False 是否更新Redis:False======从本地缓存取值 未查询到 UserId=3从DB取值 UserId=3 Name=BigOrange_3===================================================数据源:LocalCache, DataBase 是否更新本地:True 是否更新Redis:False======从本地缓存取值 未查询到 UserId=3从DB取值 UserId=3 Name=BigOrange_3设置值到本地缓存:useId = 3===================================================数据源:LocalCache, DistributeCache, DataBase 是否更新本地:False 是否更新Redis:False======从本地缓存取值 UserId=1 Name=BigOrange_1===================================================数据源:LocalCache, DistributeCache, DataBase 是否更新本地:False 是否更新Redis:False======从本地缓存取值 未查询到 UserId=2从Redis缓存取值 UserId=2 Name=BigOrange_2从DB取值 UserId=2 Name=BigOrange_2===================================================数据源:LocalCache, DistributeCache, DataBase 是否更新本地:True 是否更新Redis:False======从本地缓存取值 未查询到 UserId=2从Redis缓存取值 UserId=2 Name=BigOrange_2设置值到本地缓存:useId = 2从DB取值 UserId=2 Name=BigOrange_2设置值到本地缓存:useId = 2===================================================数据源:LocalCache, DistributeCache, DataBase 是否更新本地:False 是否更新Redis:False======从本地缓存取值 未查询到 UserId=3从Redis缓存取值 未查询到 UserId=3从DB取值 UserId=3 Name=BigOrange_3===================================================数据源:LocalCache, DistributeCache, DataBase 是否更新本地:True 是否更新Redis:False======从本地缓存取值 未查询到 UserId=3从Redis缓存取值 未查询到 UserId=3从DB取值 UserId=3 Name=BigOrange_3设置值到本地缓存:useId = 3===================================================数据源:LocalCache, DistributeCache, DataBase 是否更新本地:False 是否更新Redis:True======从本地缓存取值 未查询到 UserId=3从Redis缓存取值 未查询到 UserId=3从DB取值 UserId=3 Name=BigOrange_3设置值到Redis缓存:useId = 3===================================================数据源:LocalCache, DistributeCache, DataBase 是否更新本地:True 是否更新Redis:True======从本地缓存取值 未查询到 UserId=3从Redis缓存取值 未查询到 UserId=3从DB取值 UserId=3 Name=BigOrange_3设置值到Redis缓存:useId = 3设置值到本地缓存:useId = 3=============================================
四、总结一下
类似上面的用户信息,可能对于不同系统、不同性能要求,获取方式会有所不同。
打个比方:对于一个后台管理系统,用户信息获取是一个低频操作,可能只需要从数据库中获取,此时一般后台系统不会设置本地缓存和分布式缓存,而对于一个接口系统,可能每天有几百万的访问量,此时如果只从数据库获取,很难承受,所以要利用到分布式缓存和本地缓存。层次越多那么变化和组合也就越多,但是每个实体的存取如果都各自实现自己的方式,又比较浪费,所以如果能抽象出一套方法,只需要告诉方法存取的方式,然后得到自己想要的数据,或许这样是比较好的方式,而具体怎么拿、怎么存,还是由调用的人去给出,这样可以应对复杂的规则。这也是为什么要使用这么多委托的原因,由于像上面获取和设定User缓存的方式多种多样,这么做可以把具体的获取和设置缓存的操作开放给使用者。在系统重构方面上,可以将一些通用的方法抽象出来,相对成本较低,扩展性好一些。
五、题外话
上面的代码中对于更新数据,没有做线程安全处理,多个进程去更新分布式缓存、同一进程的多个线程去更新本地缓存,可能都需要进行锁操作。
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。