因MemoryCache闹了个笑话
前言
是这么一回事:
我正在苦思一个业务逻辑,捋着我还剩不多的秀发,一时陷入冥想中......
突然聊天图标一顿猛闪,打开一看,有同事语音;
大概意思是:同事把项目中Redis部分缓存换成MemoryCache/Memcached,还强调MemoryCache/Memcached的效率是Redis的2~5倍;
当时我想到的是Memcached,听到的似乎也是,心想:怎么可能,就算有性能差,也不至于那么多;
因为当时同事代码还没提交,然后就陷入讨论ing,最后还是没聊通,我就跑到同事那当面沟通(要去打架吗,不不不,文明人);
沟通中.....,好几分钟过去了,突然同事说:他用的是微软的MemoryCache;
虽然从读音上我还没区分出来,但一听微软,我就感觉我成笑话啦;
然后赶紧让同事打开代码,我擦,真成笑话啦,还理直气壮的沟通了好几十分钟。
为什么会那么“理直气壮”?
- 对Memcached(听错的)性能高于Redis的2~5倍产生重大怀疑,经验告诉我不可能,除非Redis用法有问题;
- 同事把Redis换成Memcached(听错的),那肯定不行的,前期的技术选型,Memcached不太符合项目应用场景;
最后因为MemoryCache成就了一场笑话,那MemoryCache和Memcached有什么区别呢?
- MemoryCache不是分布式缓存,是基于程序存储在内存中的;是微软封装好的内存缓存库,合理利用CPU,性能好;由于基于程序分配内存,使用时避免了网络通讯的消耗。
- Memcached是分布式缓存,是存储在公共机器上的,供不同程序使用的,存在一定的网络传输消耗。
这样比较感觉有点勉强,虽然Memcached是分布式的,但也是基于内存的,在数据存储内存的逻辑还是不同的,不过这里不打算讲解源码,我要说应用,哈哈哈。
附加-为什么Redis让同事感觉性能不好
真实场景是这样的,客户端开启多线程频繁读取Redis数据,当访问比较多时,导致Redis读取数据超过20毫秒,对于Web项目来说其实这还好,20毫秒的响应用户根本无法感知。但对于一个高性能要求的服务程序来说,对通讯要求就比较高,所以简单分析了一下拖慢的原因,大概以下两点:
客户端增多,导致一个常用Key对应的数据变大(其实不大,只是相对大,稍微影响了读取速度,毫秒级别);
解决方案:同事使用MemoryCache多做一层缓存,将这个常用Key直接存在内存中,提高读取性能;
使用类似于Keys * 的命令频繁获取数据,导致有些命令执行在20毫秒左右(慢日志中可以看到);
解决方案:改用Scan类似命令获取数据;
Redis自身的持久化耗时;
解决方案:适当调整Redis持久化策略,让持久化频率没那么高;
正文
回归正题,既然说到MemoryCache,就来简单聊聊,主要分享在项目实战中如何使用;
主要依赖包:Microsoft.Extensions.Caching.Memory;
MemoryCache的使用很简单,就是在调用方法设置和获取值就对啦;来直接看Demo吧;
1. 控制台Demo
其实有很多程序是基于后台服务运行的,并不都是Web,所以写了一个控制台的Demo,方便小伙伴参考;
1.1 引入相关包,项目中使用了Autofac作为依赖注入和其切面编程,则需要引入相关的依赖包,项目结构和包引入如下图:

1.2 编写示例代码及注册相关服务
IUserService就是简单接口;

接口和方法上标注的Intercept和MyCache特性不是必须的,接下来会说;
UserService对接口的实现;

MyCacheAttribute自定义特性,用于标识,里面没有任何逻辑处理;

MyInterceptor自定义拦截器,面向切面的逻辑代码在这里处理;

代码完了,就开始使用Autofac注册服务,进行使用啦,如下:

注:Autofac不是必须的,根据自己需要进行选择使用,这里是为了要使用Autofac的切面编程功能。
1.3 两种方式进行缓存处理
通常在非Web程序中,有以下两种方式进行缓存处理:
代码嵌入到业务逻辑,在真实业务逻辑处进行缓存获取或设置;

这样很大一个缺点是每一个缓存的数据都需要手动到指定业务逻辑中添加缓存处理,代码后期不好维护,缓存功能的开启和关闭也不好控制,需要修改代码进行满足。
面向切面编程,无需嵌入多余代码到业务中
通过面向切面的思想,以动态代理的原理拦截方法,在方法前后进行处理,如下:

缓存逻辑直接放在拦截器中处理即可,如下:

注册服务时,开启Autofac的面向切面功能即可

运行看效果,第二次都是从缓存中获取数据,美美哒:

注:推荐使用面向切面的形式进行处理,这样缓存功能可插拔,代码维护性也好。
2.WebApiDemo(项目名称为:MemoryCacheWebApiDemo)
在WebApi中使用就比较简单啦,关于MemoryCache的依赖包已经集成在框架中,如果需要使用,直接注册服务就可以用啦;通常在WebApi中进行缓存处理的方式有三种:
- 中间件形式:通过自定义中间件进行缓存逻辑操作;
- 过滤器形式:使用MVC相关过滤器进行缓存逻辑操作;
- 业务层面向切面形式:面向切面的方式,在业务层做缓存,集成Autofac即可,和控制台Demo的面向切面一样;
这里就用过滤器的形式进行Demo演示,走起来~~~
2.1 编写过滤器
缓存如果能在最前面处理,就优先在最前面,所以使用的ResourceFilter过滤器;关于过滤器之前写过一篇文章很详细(跟我一起学.NetCore之MVC过滤器,这篇看完走路可以仰着头走),这里就不赘述了。

然后在Startup.cs中注册服务和将自定义过滤器注册即可,如下:

然后启动运行,多次请求调试看看效果(小伙伴自己在过滤器中调试即可);
在WebApi中也可以使用业务层面向切面的方式进行相关业务处理,集成Autofac即可;小伙伴可以参照这篇文章(跟我一起学.NetCore之Asp.NetCore中集成Autofac扩展)。 关于AOP面向切面编程这块,后续单独整理一篇分享;
源码地址:https://github.com/zyq025/DotNetCoreStudyDemo
总结
在一些开发项目中,可能会使用Dictionary进行数据缓存,如果是这样,可以尝试使用MemoryCache,性能合理利用CPU,而且还是线程安全的;另外在高并发场景,可以用其作为多级缓存,因为MemoryCache还能设置过期时间,搭配Redis配合使用,效果杠杠的。
一个被程序搞丑的帅小伙,关注"Code综艺圈",跟我一起学~~~

因MemoryCache闹了个笑话的更多相关文章
- AOP(面向切面编程)大概了解一下
前言 上一篇在聊MemoryCache的时候,用到了Autofac提供的拦截器进行面向切面编程,很明显能体会到其优势,既然涉及到了,那就趁热打铁,一起来探探面向切面编程. 正文 1. 概述 在软件业, ...
- Polly-故障处理和弹性应对很有一手
前言 对于运行中的系统,可以说百分百的小伙伴会经常遇见以下问题: 网络不通,突然又好了: 服务器宕机了: 调用服务接口超时了: 调用接口报错啦: 通讯信息发送失败需要重发: 以上只是列举了一些常遇到的 ...
- 使用缓存(Cache)的几种方式,回顾一下~~~
前言 如今缓存成为了优化网站性能的首要利器,缓存使用的好,不仅能让网站性能提升,让用户体验变好,而且还能节约成本(增加一台缓存服务器可能就节约好几台机器):那平时小伙伴们都使用哪些缓存方式呢?这里就来 ...
- 一些对数学领域及数学研究的个人看法(转载自博士论坛wcboy)
转自:http://www.math.org.cn/forum.php?mod=viewthread&tid=14819&extra=&page=1 原作者: wcboy 现在 ...
- 基于Laravel+Swoole开发智能家居后端
基于Laravel+Swoole开发智能家居后端 在上一篇<Laravel如何优雅的使用Swoole>中我已经大概谈到了Laravel结合Swoole的用法. 今天,我参与的智能家居项目基 ...
- PC硬件之我见——CPU篇
写在最前面: 最近身边很多朋友都购置电脑的想法,往往也会选择性价比较高的DIY攒机方式.不幸的是,并不是所有人都对电脑硬件有一定的了解的,何况在现在这种社会风气下,盲目的相信商家是不明智的.所 ...
- 服务器搭建纪录linux+mysql+nginx+php
新的项目启动 第一版 首先买了阿里云,选好环境镜像包,一键安装. 第一版php打算不用框架,完全手写,主要的功能点 数据交互和图片传输. 后台搭建好后,使用PHP的Laravel, web端还是选定b ...
- 《javascript语言精粹》——第4章函数
函数就是对象 [1].函数字面量即(函数表达式)包括四部分: 第一部分:保留字function: 第二部分:函数名称,可有可无: 第三部分:包围在一对小括号的一组参数,参数用逗号隔开: 第四部分:包围 ...
- Spring+SpringMVC+MyBatis+easyUI整合优化篇(六)easyUI与富文本编辑器UEditor整合
日常啰嗦 本来这一篇和接下来的几篇是打算讲一下JDBC和数据库优化的,但是最近很多朋友加我好友也讨论了一些问题,我发现大家似乎都是拿这个项目作为练手项目,作为脚手架来用的,因此呢,改变了一下思路,JD ...
随机推荐
- Atcoder Beginner Contest 168 D - .. (Double Dots) (BFS)
题意:有\(n\)个房间,在这些房间中两两连\(m\)次条边,问除了第一个房间,其他房间走到第一个房间的最短路径,输出这个房间所连的上一个房间,如果走不到,输出\(no\). 题解:刚开始我写了一个d ...
- JavaScript——六
magin和padding的区别:https://www.cnblogs.com/zxnn/p/8186225.html magin:兄弟之间的 padding:父子关系 body和网页边框左右距离上 ...
- WPF Dispatcher 频繁调度导致的性能问题
问题 WPF Dispatcher 提供了UI线程之外的线程异步操作(请求)UI变化.一次Invoke/BeginInvoke调用产生一个DispatcherOperation,将挂在调度队列中,按照 ...
- SpringBoot整合Swagger初探
当下的Web项目大都采用前后端分离+分布式微服务的架构.前后端分离式的开发中,前端开发人员要与后端开发人员协商通信接口,约定接口规范.因此对于开发人员来说能够维持一份及时更新且完整全面的API文档会大 ...
- leetcode15 三数之和 双指针
注意题目没要求数字只能用一次 a + b + c = 0 即为 -b=a+c,同时要求数字不全为正(然后发现a+b+c就行...不过多想想没坏处嘛) 先处理特殊情况,然后 先排序 注意不重复,只需要有 ...
- spring boot集成mybatis只剩两个sql 并提示 Cannot obtain primary key information from the database, generated objects may be incomplete
前言 spring boot集成mybatis时只生成两个sql, 搞了一个早上,终于找到原因了 找了很多办法都没有解决, 最后注意到生成sql的时候打印了一句话: Cannot obtain pri ...
- JVM系列之一 JVM的基础概念与内存区域
前言 作为一名 Java 语言的使用者,学习 JVM 有助于解决程序运行过程中出现的问题.写出性能更高的代码. 可以说:学好 JVM 是成为中高级 Java 工程师的必经之路. 有感于从未整理归纳 J ...
- Rollup & Webpack & Parcel
Rollup & Webpack & Parcel package bundler https://x-team.com/blog/rollup-webpack-parcel-comp ...
- HTML5 stream video player
HTML5 stream video player Aliplayer https://player.alicdn.com/aliplayer/index.html https://help.aliy ...
- 「NGK每日快讯」12.29日NGK第56期官方快讯!