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

如果真要细说缓存的好处,还真是不少,但是在实际的应用中,很多时候使用缓存的时候,总是那么的不尽人意。换句话说,假设本来采用缓存,可以使得性能提升为 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 的配置问题(对缓存内存的限制),我们有机会专门为大家分享这方面的知识。

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

转载于:https://www.cnblogs.com/hdx1027/p/4942348.html

【转载】使用缓存的9个误区(上)相关推荐

  1. macos 清空dns缓存_如何在macOS上重置DNS缓存

    macos 清空dns缓存 If you work on websites, you'll occasionally need to reset your computer's DNS cache, ...

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

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

  3. 转载:缓存 Cache

    /// <summary>         /// 缓存函数         /// </summary>         /// <param name="p ...

  4. 操作系统:基于页面置换算法的缓存原理详解(上)

    概述: 作为一个学计算机的一定听过缓存(注意这里是缓存,不是缓冲).比如我们在登录网页时,网页就可以缓存一些用户信息:比如我们在写界面代码的时候,可能就会遇到界面的绘制是基于一些缓存算法的.所以,了解 ...

  5. 并发环境下的缓存容器性能优化(上):不可变的哈希表

    我们在项目中经常会遇到这样的场景:一些信息读取开销较大,但只需要生成一次便可反复使用,因此我们会将其永久地缓存起来.例如在ASP.NET MVC中,系统会根据Controller的名称来缓存对应的元数 ...

  6. [转载]用缓存服务器负载均衡 提数据库查询效率

    根据一些专家的调查分析,发现企业在使用数据库的时候,90%以上主要用来查询.有些企业这个比例甚至更高.也就说,用户对数据库的操作,其实更新操作占的比例很少.大部分的操走都只是查询操作.如一些论坛,大部 ...

  7. 转载:sql2005 管道的另一端上无任何进程解决方法

    已成功与服务器建立连接,但是在登录过程中发生错误.   (provider:   共享内存提供程序,   error:   0   -   管道的另一端上无任何进程. 我自己发现的问题是有相关服务没开 ...

  8. 设计一套基于NHibernate二级缓存的MongoDB组件(上)

    摘要:NHibernate Contrib 支持很多第三方的二级缓存,如SysCache,MemCache,Prevalence等等,但是没有MongoDB的,于是自己扩展了一个支持MongoDB的缓 ...

  9. [转载]Memcached缓存服务的简单安装

    1.Linux下的安装方法 下载:wget http://memcached.org/latest tar -zxvf memcached-1.x.x.tar.gz cd memcached-1.x. ...

最新文章

  1. 一个简单的Dockerfile
  2. 拖链电缆 机器人电缆_洞头高柔性拖链网线标柔电缆
  3. hive sql循环_hive存储过程
  4. sublime支持虚拟环境python2.7
  5. 2017年计算机组成原理1254,2018年7月试卷号1254计算机组成原理A.pdf
  6. html5小游戏是用js做的吗,谁说做H5动画和游戏一定要canvas?
  7. CentOS7安装mysql8并配置
  8. hadoop yarn 获取日志_「大数据」「Hadoop」-安装及数据目录
  9. 数据库篇之[bsp_orders]数据表-BrnShop1.9升级至2.1升级说明(非官方版本)
  10. php 精准定位到街道,ip地址查询精确到街道_ip查询详细地址带地图
  11. MapGIS出现“存储服务错误,可能未启动”问题解决方案
  12. 计算机批注的概念,批注
  13. [转]ASP.NET 安全认证(三): 用Form 表单认证实现单点登录
  14. 软件设计师---软件工程
  15. myftpadmin+proftpd+mysql架设ftp服务器_proftpd – 碎言碎语
  16. 【Verilog基础】【总线协议】AHB和AHB-Lite的区别?AMBA2.0和AMBA3.0的区别?目前常见的几种AMBA协议内容?
  17. vip2-day22 MySQL 基础语句
  18. CATIA V6二次开发——Automation之对象
  19. 东京秋叶原谢幕,彼岸电子城路在何方
  20. 怎么恢复360强力删除的文件?数据恢复这样做

热门文章

  1. ARP监听渗透内网的方法
  2. elasticsearch查询操作(使用marvel插件)
  3. linux系统反应优化,细说Linux系统优化-实践篇【转载】
  4. JUnit5 @AfterAll注解示例
  5. mp4剪辑器_想学视频剪辑,可是专业的视频软件太难,来试试这软件吧!
  6. 错误: 找不到或无法加载主类 helloworld_你还不知道Java的类加载机制?
  7. map for循环_阿里面试问我hashMap的循环姿势有哪几种?
  8. mysql datapump_mysqlpump参数详解
  9. 资深前端工程师:裁人后,我总结了 7 个必备技能
  10. 关于慢开始、拥塞控制、快重传、快恢复算法的理解