使用缓存的9大误区(上)(转)

如果说要对一个站点或者应用程序经常优化,可以说缓存的使用是最快也是效果最明显的方式。一般而言,我们会把一些常用的,或者需要花费大量的资源或时间而产生的数据缓存起来,使得后续的使用更加快速。

  如果真要细说缓存的好处,还真是不少,但是在实际的应用中,很多时候使用缓存的时候,总是那么的不尽人意。换句话说,假设本来采用缓存,可以使得性能提升为100(这里的数字只是一个计量符号而已,只是为了给大家一个“量”的体会),但是很多时候,提升的效果只有80,70,或者更少,甚至还会导致性能严重的下降,这个现象在使用分布式缓存的时候尤为突出。

  在本篇文章中,我们将为大家讲述导致以上问题的9大症结,并且给出相对应的解决方案。文章以.NET为例子进行代码的演示,对于来及其他技术平台的朋友也是有参考价值的,只要替换相对应的代码就行了!

  为了使得后文的阐述更加的方便,也使得文章更为的完整,我们首先来看看缓存的两种形式:本地内存缓存,分布式缓存。

  首先对于本地内存缓存,就是把数据缓存在本机的内存中,如下图1所示:

  从上图中可以很清楚的看出:

  • 应用程序把数据缓存在本机的内存,需要的时候直接去本机内存进行获取。
  • 对于.NET的应用而言,在获取缓存中的数据的时候,是通过对象的引用去内存中查找数据对象的,也就说,如果我们通过引用获取了数据对象之后,我们直接修改这个对象,其实我们真正的是在修改处于内存中的那个缓存对象。

  对于分布式的缓存,此时因为缓存的数据是放在缓存服务器中的,或者说,此时应用程序需要跨进程的去访问分布式缓存服务器,如图2:

  不管缓存服务器在哪里,因为涉及到了跨进程,甚至是跨域访问缓存数据,那么缓存数据在发送到缓存服务器之前就要先被序列化,当要用缓存数据的时候,应用程序服务器接收到了序列化的数据之后,会将之反序列化。序列化与反序列化的过程是非常消耗CPU的操作,很多问题就出现在这上面。

  另外,如果我们把获取到的数据,在应用程序中进行了修改,此时缓存服务器中的原先的数据是没有修改的,除非我们再次将数据保存到缓存服务器。请注意:这一点和之前的本地内存缓存是不一样的。

  对于缓存中的每一份数据,为了后文的讲述方面,我们称之为“缓存项“。

  普及完了这两个概念之后,我们就进入今天的主题:使用缓存常见的9大误区:

  1. 太过于依赖.NET默认的序列化机制
  2. 缓存大对象
  3. 使用缓存机制在线程间进行数据的共享
  4. 认为调用缓存API之后,数据会被立刻缓存起来
  5. 缓存大量的数据集合,而读取其中一部分
  6. 缓存大量具有图结构的对象导致内存浪费
  7. 缓存应用程序的配置信息
  8. 使用很多不同的键指向相同的缓存项
  9. 没有及时的更新或者删除再缓存中已经过期或者失效的数据

  下面,我们就每一点来具体的看看!

太过于依赖.NET默认的序列化机制

  当我们在应用中使用跨进程的缓存机制,例如分布式缓存memcached或者微软的AppFabric,此时数据被缓存在应用程序之外的进程中。每次,当我们要把一些数据缓存起来的时候,缓存的API就会把数据首先序列化为字节的形式,然后把这些字节发送给缓存服务器去保存。同理,当我们在应用中要再次使用缓存的数据的时候,缓存服务器就会将缓存的字节发送给应用程序,而缓存的客户端类库接受到这些字节之后就要进行反序列化的操作了,将之转换为我们需要的数据对象。

  另外还有三点需要注意的就是:

  • 这个序列化与反序列化的机制都是发生在应用程序服务器上的,而缓存服务器只是负责保存而已。
  • .NET中的默认使用的序列化机制不是最优的,因为它要使用反射机制,而反射机制是是非常耗CPU的,特别是当我们缓存了比较复杂的数据对象的时候。

  基于这个问题,我们要自己选择一个比较好的序列化方法来尽可能的减少对CPU的使用。常用的方法就是让对象自己来实现ISerializable接口。

  首先我们来看看默认的序列化机制是怎么样的。如图3:

  然后,我们自己来实现ISerializable接口,如下图4所示:

  我们自己实现的方式与.NET默认的序列化机制的最大区别在于:没有使用反射。自己实现的这种方式速度可以是默认机制的上百倍。

  可能有人认为没有什么,不就是一个小小的序列化而已,有必要小题大做么?

  在开发一个高性能应用(例如网站)而言,从架构,到代码的编写,以及后面的部署,每一个地方都需要优化。一个小问题,例如这个序列化的问题,初看起来不是问题,如果我们站点应用的访问量是百万,千万,甚至更高级别的,而这些访问需要去获取一些公共的缓存的数据,这个之前所谓的小问题就不小了!

  下面,我们来看第二个误区。

缓存大对象

  有时候,我们想要把一些大对象缓存起来,因为产生一次大对象的代价很大,我们需要产生一次,尽可能的多次使用,从而提升响应。

  提到大对象,这里就很有必要对其进行一个比较深入的介绍了。在.NET中,所谓的大对象,就是指的其占用的内存大于了85K的对象,下面通过一个比较将问题说清楚。

  如果现在有一个Person类的集合,定义为List<Person>,每个Person对象占用1K的内存,如果这个Person集合中包含了100个Person对象实例,那么这个集合是否是大对象呢?

  回答是:不是!

  因为集合中只是包含的Person对象实例的引用而言,即,在.NET的托管堆上面,这个Person集合分配的内存大小也就是100个引用的大小而言。

  然后,对于下面的这个对象,就是大对象了: byte[] data = new byte[87040](85 * 1024 = 87040)。

  说到了这里,那就就谈谈,为什么说:产生一次大对象的代价很大

  因为在.NET中,大对象是分配在大对象托管堆上面的(我们简称为“大堆”,当然,还有一个对应的小堆),而这个大堆上面的对象的分配机制和小堆不一样:大堆在分配的时候,总是去需找合适的内存空间,结果就是导致出现内存碎片,导致内存不足!我们用一个图来描述一下,如图5所示:

  上图非常明了,在图5中:

  • 垃圾回收机制不会在回收对象之后压缩大堆(小堆是压缩的)。
  • 分配对象的时候,需要去遍历大堆,去需找合适的空间,遍历是要花成本的
  • 如果某些空间小于85K,那么就不能分配了,只能白白浪费,也导致内存碎片。

  讲完了这些之后,我们言归正传,来看看大对象的缓存。

  正如之前讲过,将对象缓存和读取的时候是要进行序列化与反序列化的,缓存的对象越大(例如,有1M等),整个过程中就消耗更多的CPU。

  对于这样的大对象,要看它使用的是否很频繁,是否是公用的数据对象,还是每个用户都要产生的。因为我们一旦缓存了(特别在分布式缓存中),就需要同时消耗缓存服务器的内存与应用程序服务器的CPU。如果使用的不频繁,建议每次生成!如果是公用的数据,那么建议多多的测试:将生产大对象的成本与缓存它的时候消耗的内存和CPU的成本进行比较,选择成本小的!如果是每个用户都要产生的,看看是否可以分解,如果实在不能分解,那么缓存,但是及时的释放!

使用缓存机制在线程间进行数据的共享

  当数据放在缓存中的时候,我们程序的多个线程都可以访问这个公共的区域。多个线程在访问缓存数据的时候,会产生一些竞争,这也是多线程中常常发生的问题。

  下面我们分别从本地内存缓存与分布式缓存两个方面介绍竞争的带来的问题。

  看下面的一段代码:

  对于本地内存缓存,对于上面的代码,当这个三个线程运行起来之后,在线程1中,item的值很多时候可能为1,线程2可能是2,线程3可能是3。当然,这不一定!只是大多数情况下的可能值!

  如果是对于分布式缓存,就不好说了!因为数据的修改不是立刻发生在本机的内存中的,而是经过了一个跨进程的过程。

  有一些缓存模块已经实现了加锁的方式来解决这个问题,例如AppFabric。大家在修改缓存数据的时候要特别注意这一点。

认为调用缓存API之后,数据会被立刻缓存起来

  有时候,当我们调用了缓存的API之后,我们就会认为:数据已经被换成了,之后就可以直接读取缓存中的数据。尽管情况很多时候如此,但是不是绝对的!很多的问题就是这样产生的!

  我们通过一个例子来讲解。

  例如,对于一个ASP.NET 应用而言,如果我们在一个按钮的Click事件中调用了缓存API,然后在页面呈现的时候,就去读取缓存,代码如下:

  上面的代码照道理来说是对的,但是会发生问题。按钮点击之后回传页面,然后呈现页面的时候显示数据,流程没有问题。但是没有考虑到这样一个问题:如果服务器的内存紧张,而导致进行服务器内存的回收,那么很有可能缓存的数据就没有了!

  这里有朋友就要说了:内存回收这么快?

  这主要看我们的一些设置和处理。

  一般而言,缓存机制都是会设置绝对过期时间与相对过期时间,二者的区别,大家应很清楚,我这里不多说。对于上面的代码而言,如果我们设置的是绝对过期时间,假设1分钟,如果页面处理的非常慢,时间超过了1分钟,那么等到呈现的时候,可能缓存中的数据已经没有了!

  有时候,即使我们在第一行代码中缓存了数据,那么也许在第三行代码中,我们去缓存读取数据的时候,就已经没有了。这或许是因为在服务器内存压力很大的,缓存机制将最少访问的数据直接清掉。或者服务器CPU很忙,网络也不好,导致数据没有被即使的序列化保存到缓存服务器中

  另外,对于ASP.NET而言,如果使用了本地内存缓存,那么,还涉及到IIS的配置问题(对缓存内存的限制),我们有机会专门为大家分享这方面的知识。

  所以,每次在使用缓存数据的时候,要判断是否存在,不然,会有很多的“找不到对象”的错误,产生一些我们认为的“奇怪而又合理的现象”。

关于作者

  汪洋,现任惠普架构师、信息分析师《NET应用架构设计:模式、原则与实践》作者。上海益思研发管理咨询有限公司首席软件架构专家,软件咨询组副组长。

转载于:https://www.cnblogs.com/xu-xiang/p/5890528.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/573135.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

cmake取消宏定义_Excel基础丨取消excel中宏安全提示框

很多高手都喜欢使用“宏”命令来提高办公工作效率。但在 Microsoft Office 程序中使用宏时&#xff0c;总会弹出宏安全警告&#xff0c;这让使用者倍感麻烦。而如果把宏的安全级设置为“低”&#xff0c;就可以取消excel中宏安全提示框了&#xff0c;又增加了恶意代码和病毒攻击…

ctypes 模块

ctypes赋予了python类似于C语言一样的底层操作能力&#xff0c;通过ctypes模块可以调用动态链接库中的导出函数、构建复杂的c数据类型。 ctypes提供了三种不同的动态链接库加载方式&#xff1a;cdll(),windll(),oledll()。 HelloWorld.py&#xff1a; 1 import ctypes #导入c…

5b计算机联锁系统_力控科技油库调度管理系统应用案例

一 、 项目概述"油库储油罐区具有分布空间范围广、安全防爆要求高、监控点多、布线复杂&#xff0c;自动化系统的水平和垂直集成难度大等特点。系统采用先进测控与管理技术&#xff0c;围绕储油罐区自动计量、监测与管理&#xff0c;进行储油罐区监测控制与数据采集系统的…

集合框架(九)----Map

从这篇开始就要学习Map了&#xff01; 先来看一下Map的继承体系&#xff1a; 如君所见&#xff0c;Maps的体系没有像Collections那么庞大 在接下来的两篇博文中将分别介绍HashMap和TreeMap 至于WeakHashMap,个人感觉文档中说的相当含糊&#xff0c;理解起来很有难度&#xff0c…

cpu java poi 导出_让 Java 开发更简单,提高工作效率 | Gitee 项目推荐

1、项目名称&#xff1a;基于 Spring Boot 的权限管理系统项目简介&#xff1a;Good 权限管理系统是作者学习 springBoot 时基于 springBoot 开发的一套轻量级的权限系统&#xff0c;其目的是形成一套属于自己的通用的开发框架 以后来项目的时候就可以直接基于此平台进行开发&a…

砝码称重2

【题目描述】 有n个砝码&#xff0c;现要称一个质量为m的物体&#xff0c;询问最少需要挑出几个砝码来称&#xff0c;一个砝码最多只能挑一次。 【输入描述】第一行输入两个整数n和m&#xff1b; 接下来n行&#xff0c;每行输入一个整数表示砝码的重量。 【输出描述】输出一个整…

给图片下方加水印_别再看不起美图秀秀啦,想要做长图,批量加水印,用它超级方便...

Hello大家好&#xff0c;我是撒娇的小肉片。时隔超长时间的更新&#xff0c;不知道还有多少人记得我哈哈。今天想要和大家分享的是如何批量加水印&#xff0c;如何拼成长图&#xff0c;操作简单&#xff0c;你值得拥有哦~最重要的是免费&#xff01;免费&#xff01;完全免费&a…

加载顺序_Java的web.xml组件加载顺序

在配置项目组件的过程中&#xff0c; 了解Tomcat加载组件顺序很有必要。 例如某些框架如Quartz的集群功能需要数据库的支持&#xff0c; 数据库的加载肯定要在框架组件加载之前。经过查阅和Debug发现&#xff0c; web.xm组件加载顺序为&#xff1a;context-param -> listene…

20145326蔡馨熠《信息安全系统设计》第2周学习总结

20145326蔡馨熠《信息安全系统设计》第2周学习总结 教材学习内容总结 一、计算机系统与链接 信息就是位上下文&#xff0c;都是由一串位表示的&#xff0c;区分不同数据对象的唯一方法是我们读到这些数据对象时的上下文。ASCII字符构成的文件是文本文件&#xff0c;所有其他文件…

c#解析json字符串数组_C#解析JSON字符串总结

JSON文件读取到内存中就是字符串&#xff0c;.NET操作JSON就是生成与解析JSON字符串。操作JSON通常有以下几种方式&#xff1a;1. 原始方式&#xff1a;按照JSON字符串自己来解析。2. 通用方式【★★★★★】&#xff1a;这种方式是使用开源的类库Newtonsoft.Json(下载地址http…

/etc/fstab 文件配置项简单介绍

Untitled本文来源&#xff1a;https://wiki.archlinux.org/index.php/Fstab_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87) 查看文件系统分区表。fstab 全称为file systems table cat /dev/fstab /etc/fstab# <file system> <dir> <type> <…

为什么不敢和别人竞争_净空法师开示:学老实,在社会上不能跟人竞争吗? - 如是我闻佛教网...

净空法师开示 - 学老实&#xff0c;在社会上不能跟人竞争吗&#xff1f;下面&#xff0c;「又《十住毘婆沙论》曰&#xff1a;若人疾欲至不退转地者&#xff0c;应以恭敬心&#xff0c;执持称名号。」这几句话在这个地方提醒我们&#xff0c;假使有个人想很快速就能得到不退转…

python中a and b什么意思_Python中的a+=b和a=a+b之间的区别是什么?

我一直以为ab和aab是一样的&#xff0c;毕竟效果是一样的嘛。后来我发现并不是这么回事&#xff1f;我发现当变量是不可变对象时&#xff0c;ab和aab是一样的&#xff0c;不过我注意到了前后两个a已经不是同一个对象了。而当变量是可变对象时&#xff0c;虽然ab和aab的效果是一…

快速提高 Vi/Vim 使用效率的原则与途径

Vi/Vim 是所有 Unix/Linux 操作系统默认配备的编辑器。因其强大的功能和高效的操作&#xff0c;Vi/Vim 也成为众多 Unix/Linux 用户、管理员必须掌握并熟练使用的编辑工具之一。尤其是在没有图形界面的情况下&#xff0c;更是离不开 Vi/Vim。Vi/Vim 命令非常多、用法极为灵活&a…

5和6 objbc oracle_Oracle测试题

1) PL/SQL块中可以使用下列(BC)命令。(选择两项)a) TRUNCATEb) DELETEc) SA VEPOINTd) ALTER TABLE2) 授予sa用户在SCOTT.EMP表中SAL列的更新权限的语句是()〔选择一项〕a) GRANT CHANGE ON SCOTT.EMP TO SAb) GRANT UPDA TE ON SCOTT.EMP(SAL) TO SAc) GRANT UPDA TE (SAL) ON…

Bash 入门教程10-处理用户输入

用户输入参数是程序运行的必要条件&#xff0c;如何在Bash中调用输入的参数&#xff0c;将Bash和GCC联合调试&#xff0c;这是linux工程师必备的一个能力。 $./shawn 10 30 这是一个很典型的例子&#xff0c;后面两个就是传入bash的命令行参数。 bash shell 会将一些成为位置参…

存放在外存上的数据关机后_小鑫话题 | 惊了!关机后SSD会丢数据?

今天小鑫在群里看到一个很有趣的话题&#xff0c;是关于SSD和HDD哪个更好的讨论。这个话题小鑫都已经见过不下十次了&#xff0c;但是今天小鑫看到了一句以前从没有看到的言论。(某交流群截图)看到这里&#xff0c;小鑫觉得网络上的“标题党”&#xff0c;可能已经在大做文章了…

python:装饰器

1.情景引入。 现在已经完成了一个python项目&#xff0c;但是我们为了安全起见&#xff0c;我们需要加入验证机制。不是所有人都能调用函数 原始源代码def f1():print f1def f2():print f2def f3():print f3 由于必须遵循开发闭包的原则&#xff0c;所以我们应该尽可能的是不去…

mysql sys exec_python - 使用MySQL UDF执行命令-sys_exec不起作用 - 堆栈内存溢出

我正在尝试从MariaDB服务器运行python脚本。 我已经为此安装了mysqludf库。 我正在尝试使用sys_exec函数运行脚本&#xff0c;但是没有结果。 我已经创建了测试文件&#xff0c;它只是一个带有信息“ DONE”的.txt文件。我以root用户身份执行命令&#xff0c;因此我拥有所有特权…

2、Redis入门介绍

1、什么是Redis Redis&#xff1a;REmote DIctionary Server(远程字典服务器) 是完全开源免费的&#xff0c;用C语言编写的&#xff0c;遵守BSD协议&#xff0c;是一个高性能的(key/value)分布式内存数据库&#xff0c;基于内存运行。并支持持久化的NoSQL数据库&#xff0c;是当…