距离上一次分享已经过去了三个月;情况并没有预想中的乐观。
上篇:https://www.cnblogs.com/for-example/p/12955788.html
1、出现了什么状况
还是上一次分享的问题,EFCore多租户模式吃内存。
按照上一次我们分享的处理方式,我们进行了以下处理
- 增大MemoryCache实例的[空间?]最大值(使用新的MemoryCache实例替换掉原本的实现)
- 减小每个Model在MemoryCache中的[空间?]占用
- 设置缓存存活时间
- 配置缓存刷新时间
情况并没有得到好转,它依旧吃掉了很多内存,因为即使经过了这样的处理,对于我们来说,也没有任何的帮助,设置缓存存活时间,配置缓存的刷新时间确实是能有效的利用缓存,但在所有客户蜂拥而至的时候,依旧是每个客户都需要为其缓存Model,如果客户是错峰而来,这样确实有用,但每个客户都是三餐高频访问,总不能让人不吃饭吧。
2、我们如何设计解决方案
- 回想起我们的应用还在单体的时候,一个DBHelp,不去缓存Model,手写sql直达DB,从未遇到过内存的问题
- EFCore是因为需要缓存映射关系(Model)才吃掉很多内存。
EFCore有错吗?它没有。缓存映射关系,就是合理的设计方案,因为确实是在映射上存在差异。
我们能不能把差异点变更呢?这使我们找到了解决的灵感,我们可以通过一些处理,让原本存在的映射差异前移或者后移。
3、我们如何解决
- 我们取消了Model的缓存,让所有租户使用同一Model
- 我们在ToTable的时候使用占位符取代表名差异部位
- 我们向EFCore的上下文添加了拦截器(EFCore3x重新回归),在拦截器内截获将要执行的sql,替换掉表名里的占位符
通过以上方式,我们对EFCore屏蔽了租户差异,截获其执行的sql之后还原了租户差异,替换表名的过程是实时进行的,是简单的字符串Replace,虽然会消耗一点算力,但相比刷新缓存消耗的算力和内存,所付出的代价几乎微乎其微。
4、变更后的成效
内存和CPU都降下来了,现在我们每个服务的内存控制在180Mb左右,调用最密集的服务,内存消耗在600Mb左右(并不是EF吃掉的)
总结
预留占位符,然后替换占位符,这是最简单的方式,然而却也是最有效的方式,有时候最简单的方法便最好的办法。