数据字典在sga的哪一个组件中缓存_非功能性约束之性能(1)-性能银弹:缓存...

在《什么是架构属性》一文中提到提高「性能」的主要方式是优化,而优化的其中一个主要手段就是添加缓存!

在软件工程里有这么一句话:「没有银弹」!就是说由于软件工程的复杂性,没有任何一种技术或方法能解决所有问题!软件工程是复杂的,没有银弹!但是,软件工程中的某一个问题,是有银弹的!

在《架构风格:万金油CS与分层》一文中提到过,「 计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决」!而「缓存层」可以说是添加得最多的层!主要目的就是为了提高性能!所以,缓存可以说是「性能银弹」!

本文将探讨如下内容:

  • 缓存的作用
  • 缓存的种类
  • 缓存算法
  • 分布式缓存
  • 缓存的使用
  • 网络中的缓存
  • 应用缓存
  • 数据库缓存
  • 计算机中的缓存

从代码说起

fn longRunningOperations(){ ... // 很耗时}let result = longRunningOperations();// do other thing

我们来看上面这段伪代码,longRunningOperations是个很耗时的方法(调用一次要几十秒甚至几分钟),比如:

  • 复杂的业务逻辑计算
  • 复杂的数据查询
  • 耗时的网络操作等

对于这个方法,如果每次都去调用一次的话,会非常的影响性能,用户体验也非常的不好。

那我们该如何处理呢?

一般有几种优化方案:

  • 优化业务代码,比如:更快的数据结构和算法,更快的IO模型,建立数据库索引等
  • 简化业务逻辑,导致耗时的原因可能是业务过于复杂,可以通过简化业务逻辑的方式来减少耗时
  • 将操作的结果存储起来。例如:对于某些统计类的结果,可以先用日终定时的去执行,将结果存储到统计结果表中,查找时,直接从结果中查询即可;对于某些临时操作,可以将结果存储在内存中,再次调用时,直接从内存中获取即可。

本文主要聊聊第三种方案:使用「缓存」!

主动缓存与被动缓存

一般我们使用缓存来存储一些内容,这些内容有如下一些特点(符合一条或多条):

  • 使用较频繁
  • 变更不频繁
  • 获取较耗时
  • 多系统访问

比如,

  • 字典数据:系统很多地方都会使用字典数据,而字典数据配置完成后一般不会修改,虽然从数据库中直接获取字典数据不是很耗时,但是多了查询和网络传输,性能上还是不如直接从缓存里面取快速
  • 秒杀商品信息:在秒杀时访问量很大,从缓存(静态文件、CDN等)获取要比从数据库查询要快得多
  • 其它访问频次较多的信息:此处的其它信息是因为其缓存的处理方式与上面的字典处理有差异,下面详细说明。

对于字典数据来说,一般我们的做法是在系统启动时,将字典数据直接加载到缓存中,此类缓存数据一般没有过期时间;当修改字典时,会同时更新缓存中的内容。此类缓存称为「主动缓存」,因为其缓存数据是由用户的主动修改来触发更新的。

而对于某些信息来说,因为信息量太大,不能一次性全部加载到缓存中,且也不是太清楚哪些数据访问频次高、哪些数据访问频次低。对于这样的数据,一般的做法是:

  • 先到缓存中查找是否有访问的数据,如果有则直接返回给用户
  • 如果没有,则去溯源查找
  • 找到后将其添加到缓存中
  • 最后返回给用户

此类缓存称为「被动缓存」!其缓存的数据的过期由系统来控制。那系统如何控制呢?这就涉及到缓存置换算法!

缓存置换算法

上面说了,对于被动缓存来说,由于信息量太大,数据不能一次全部加载到缓存中,当缓存满了以后,需要新增数据时,就需要确定哪些数据要从缓存里清除,给新数据腾出空间。

用于判断哪些数据优先从缓存中剔除的算法称为「缓存(页面)置换算法」!

Wiki中列出了如下置换算法:

  • RR(Random replacement)
  • FIFO(First in first out)
  • LIFO(Last in first out)
  • MRU(Most recently used)
  • LFU(Least-frequently used)
  • LRU(Least recently used)
  • TLRU(Time aware least recently used)
  • PLRU(Pseudo-LRU)
  • LRU-K
  • SLRU(Segmented LRU)
  • MQ(Multi queue)
  • LFRU(Least frequent recently used)
  • LFUDA(LFU with dynamic aging)
  • LIRS(Low inter-reference recency set)
  • ARC(Adaptive replacement cache)
  • CAR(Clock with adaptive replacement)
  • Pannier(Container-based caching algorithm for compound objects)

一般情况下我们不会自己去实现个缓存,市面上有不少开源的缓存中间件,比如:redis,memcached。这里只简单的梳理几个常用的置换算法。

FIFO

FIFO应该算是最简单的置换算法了:

  • 它使用一个队列来维护数据
  • 数据按照加载到缓存的顺序进行排列,先加载的数据在队列头部,后加载的数据在队列尾部
  • 当缓存满了以后,从队列头部清除数据,给需要加载的数据腾出空间
  • 新数据加到队列的尾部

FIFO的实现很简单,但是其性能并不总是很好。举个简单的例子,假设一个系统需要10个缓存数据,恰巧此时5个数据在队列头部,另外5个数据不在缓存中,又恰巧此时队列又满了。按照FIFO算法,5条不在内存中的数据被加载到了缓存中,而之前的5条数据被清除了。这就需要再次将被清除的5条数据加载到缓存中。这就影响了性能。

这个问题可能会随着所分配的缓存大小的增加而增加,原本我们使用缓存是为了提高性能的,现在可能会影响性能,这种现象称为「Belady现象」!

LIFO和FIFO很类似,这里就不赘述了。

LRU

目前比较常用的置换算法称为LRU置换算法:优先替换掉「最近最少使用」的数据

  • 每个数据都被关联了该数据上次使用的时间
  • 当需要置换数据的时候,LRU选择最长时间没有使用的数据

LRU的变体有很多,例如:

  • TLRU(Time aware least recently used):大部分缓存数据是有过期时间的。PLRU从最少使用和过期时间两个维度来置换数据
  • LRU-K:多维护一个队列,用于记录所有缓存数据被访问的次数。当数据的访问次数达到K次的时候,才将数据放入缓存。当需要淘汰数据时,LRU-K会淘汰第K次访问时间距当前时间最大的数据。
  • SLRU(Segmented LRU)2Queue?:一个FIFO队列,一个LRU队列。当数据第一次访问时,将数据缓存在FIFO队列里面,当数据第二次被访问时,则将数据从FIFO队列移到LRU队列里面,两个队列各自按照自己的方法淘汰数据。
  • PLRU(Pseudo-LRU):LRU需要维护数据访问时间,占用了额外的空间,对于空间很小的设备来说,此算法太过浪费空间了。PLRU每个缓存数据只需要1bit来存储数据信息,可以达到LRU的效果。具体流程见下图:
a0ac082fd741b2d31b6a98ae569aab50.png

还有和LRU类似的MRU,LFU这里不在赘述!

缓存集群

为了提高缓存的可用性,一般我们至少会对缓存做个主备,即一个主缓存,一个从缓存。

  • 缓存的写入只可以写到主缓存
  • 主缓存同步数据到从缓存中
  • 可以从主缓存读取数据。也可以从从缓存读取数据(不必须)
  • 当主缓存挂掉了,从缓存升级为主缓存

再安全一点的做法就是做缓存集群:

  • 多台机器缓存了相同的数据,其中一台为主缓存
  • 缓存的写入只可以写到主缓存
  • 主缓存同步数据到其它缓存
  • 可以从主缓存读取数据。也可以从其它缓存中读取数据(不必须)
  • 当主缓存挂掉了,会从其它缓存服务中选择一个作为新的主缓存

分布式缓存

无论是单机缓存,主从备份还是缓存集群,都没法解决缓存大小限制的问题。因为一般缓存会使用内存,而一台机器的内存大小是有限的。当需要缓存的数据远远超过一台机器的内存大小的时候,就需要将缓存的数据分布到多台机器上。每台机器只缓存一部分数据,这就是分布式缓存。

分布式缓存可以解决一台机器缓存数据有限的问题,但是也引入了新的问题:

  • 哪些数据该缓存在哪台服务器上
  • 如何保证每台服务器缓存的数据量基本相同

一般做法是对key进行hash,然后对服务器数量进行取余,来确定数据在哪台服务器上。这解决了「哪些数据该缓存在哪台服务器上」的问题,但是却无法保证「每台服务器缓存的数据量基本相同」,因为可能多个key的hash取余后都落到了同一个服务器上,这就可能导致其中一台服务器缓存的数量很多,其它服务器缓存的数据量很少。缓存数据量多的服务器可能会内存不够用,触发数据置换,进而导致性能下降。

可以使用一致性hash环来保证服务器缓存的数据量基本相同,大致逻辑如下:

  • 将0~2^32个点均匀分配到一个圆上
  • 每个点对应一台缓存服务器
  • 缓存服务器数量是远小于2^32个的,所以多个节点对应一台缓存服务器,多出来的节点称为虚拟节点
  • 确保缓存服务器的分布均匀
  • 同样是对key进行hash
  • 对2^32进行求余
  • 结果对应到hash环上
  • 如果正好落到节点上,则数据就缓存到对应的缓存服务器上
  • 否则就存到落点前面的那个节点所对应的缓存服务器上

无处不在的缓存

上面聊的主要是应用缓存,实际上,缓存无处不在。

下面通过我们访问网站的流程,来简单梳理一下,整个过程中,哪些地方可能会用到缓存。

网络缓存

当我们在浏览器中输入URL,按下回车后。

首先,需要查找域名所对应的IP!这里就有各种缓存!

  • 浏览器缓存:浏览器会缓存DNS记录一段时间,首先会从浏览器缓存里去找对应的IP。
  • 系统缓存:如果在浏览器缓存里没有找到需要的记录,就会到系统缓存中查找记录
  • 路由器缓存:如果系统缓存中也没找到,就会到路由器缓存中查找记录
  • ISP DNS 缓存:如果还是找不到,就到ISP缓存DNS的服务器里查找。在这一般都能找到相应的缓存记录。
  • 递归搜索:如果上面的缓存都找不到,就需要从根域名服务器开始递归查找了

找到IP后,还不一定要发请求,因为你访问的资源可能之前已经访问过,已经被缓存到了浏览器缓存中。此时,浏览器直接返回缓存,而不会发送请求。

如果没有缓存,则发送请求获取资源。

后面可能会达到CDN。CDN是一种边缘缓存。在用户访问网站时,利用GSLB(Global Server Load Balance,全局负载均衡)技术将用户的访问指向距离最近的工作正常的缓存服务器上,由缓存服务器直接响应用户请求。如果CDN中找不到需要的资源,则请求可能就到了反向代理。

某些反向代理能够做到和用户来自同一个网络,那么用户访问反向代理服务器的时候,就会得到很高质量的响应速度,这样的反向代理缓存一般称为边缘缓存,而CDN在边缘缓存的基础上,使用了GSLB

一般反向代理有两个功能:

  • 隐藏源服务器,防止服务器恶意攻击。客户端感知不到代理服务器和源服务器的区别
  • 缓存,将原始服务器数据进行缓存,减少源服务器的访问压力

如果反向代理中也找不到需要的资源,请求才到达源服务器来获取资源。

服务端与数据库缓存

一般情况下,Server接收到请求后,会根据请求,组装出响应,进行返回。这个过程可能需要查询数据库、进行业务逻辑计算、页面渲染等操作。这里的每一步都可以引入缓存。

对于数据库查询来说,目前一般的持久化框架都会提供查询缓存。即对于相同的sql,第二次查询开始,可以不用再查询数据库,直接从缓存中获取第一次查询所返回的数据。节省了调用数据库查询的时间消耗。对于某些访问量很大的数据,也可以将其缓存到缓存中间件中。后续直接从缓存中间件中获取。

而数据库本身也有缓存!

33610fa46d498d382a3d5b90f6e6e8a2.png
  • 客户端发送一条查询给服务端
  • 服务端检查查询缓存,如果命中缓存,则立刻返回缓存中的结果。如果没找到,则
  • 进行sql解析、预处理、再由优化器生成对应的执行计划
  • 根据执行计划,调用存储引擎的API执行查询
  • 将结果返回给客户端

mysql的查询缓存可能会降低效率。首先,写缓存是独占模式写入。其次,假设一个查询结果被缓存了,当涉及到的其中一张表数据更新,该缓存都会被置为无效。对于频繁修改的数据,使用缓存就会降低效率。

对于业务逻辑计算来说,如果某些业务逻辑很复杂,那么可以针对结果进行缓存。可以将结果缓存到数据库或缓存中间件中。对于相同的参数的请求,第二次请求时,就不必进行计算,直接从缓存中返回结果即可。

对于页面渲染来说,某些访问量很大的页面,且数据基本不变的情况下,可以对页面进行静态化。即生成静态的页面,不必每次访问的时候都动态生成页面进行返回,而是预先生成好页面,将其存到磁盘上,当访问该页面的时候,直接从磁盘获取页面进行返回即可。或者直接将页面内容缓存到缓存中间件中,进一步提高性能。

另外,对于需要登录的Server来说,用户信息其实也是缓存下来的。不论是存到服务器Session中,还是存到了缓存中间件中。否则,每次用户访问Server都需要到数据库获取用户信息,会影响Server端性能!

计算机缓存

最后,运行系统的计算机本身也有很多的缓存!

我们都知道,一般计算机由CPU、内存、主板、硬盘、显卡、显示器、鼠标、键盘、网卡等组成!其中存储类设备包括了:云存储(例如:百度云盘,NAS等)、本地硬盘、内存、CPU中的高速缓存(我们常说的一级缓存、二级缓存和三级缓存)以及CPU寄存器。它们的速度各异,差异达数个量级。下图显示了各个设备的访问速率。

fc5bf2eec3a6286d799f9a218b849829.png
  • CPU寄存器最快,达到1ns,但只能存储几百个字节,造价也最贵
  • 高速缓存次之,也达到了10ns,可存储几十兆,造价次之。其中L1,L2,L3速度越来越慢。
  • 然后是内存,为100ns,可达GB级别,造价比缓存便宜(不过这两年的内存价格贵得离谱)
  • 硬盘访问速率为10ms级别,可达TB级别,造价可以说是白菜价了
  • 而云存储则达到了秒级,基本可以无限扩展,只要钱够

我们都知道CPU的高速缓存是「缓存」,实际上上面的设备,上层设备都可以说是下层设备的「缓存」!

在《深入理解计算机》一书中,简单的介绍了计算机执行C语言的hello world程序时的计算机流程。

  • 通过鼠标、键盘输入执行命令'./hello'
  • 输入的内容从键盘通过总线,进入寄存器,在进入内存
a9d2f10795f34759fa3a9747816607ca.png
  • 当按下回车后
  • 通过DMA技术,将目标文件,从硬盘中直接读取到内存中
359e1db376d123fc3fc06d068bd96793.png
  • 最后执行程序
  • 将hello world拷贝到寄存器
  • 再从寄存器拷贝到显示器显示
2d4d52c5229ffa43b5d5da623f961922.png

可以看到,绝大部分的操作,都是数据的拷贝!最终被CPU执行,为了数据能更快的到达CPU,就有了一层一层的「缓存」!

  • CPU寄存器里的数据是直接给CPU使用的,相当于是L1的缓存
  • L1又是L2的缓存,L2又是L3的缓存
  • L3是内存的缓存
  • 内存又是硬盘的缓存。例如:一般硬盘中的数据,都需要先加载到内存中才能被CPU使用。另外硬盘的“HMB内存缓冲技术”,可以借用内存作为硬盘的缓存。
  • 硬盘本身也是有缓存的,这是为了减少IO操作,批量的进行读写。
  • 硬盘也可以是云存储的缓存。例如在网络不太好的情况下,我们可以把电影先下载下来再看,这样就不会有卡顿的情况

总结

性能是架构设计时需要着重考虑的一个非功能性约束,而引入缓存是提高系统性能的一个简单且直接的方法。

本文从一个简单的伪代码开始,简单阐述了,缓存的作用,涉及的技术以及目前缓存的使用场景,以期能对架构设计提供一些参考。

参考资料

  • 《深入理解计算机系统》
  • 《图解TCP/IP》
  • 《高性能MySQL》
  • 《操作系统概念》
  • What really happens when you navigate to a URLCache replacement policies

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

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

相关文章

python热部署_关于Spring Cloud 框架热部署的方法

1、在对应的pom.xml 文件中添加依赖org.springframework.bootspring-boot-devtoolstrue2、注:project 中添加 spring-boot-maven-plugin,主要在eclipse中使用,idea中不需要添加此配置。【IDEA 忽略这个步骤哦(⊙o⊙)?】org.springframework.b…

html js css如何关联_会html+css+js就能把前端项目发布到多个平台

在这篇文章中,小编将给大家分享如何让自己的前端代码发布到多个常用的平台。看完这篇文章以后,你就知道了如何让你的前端代码发布到多个平台,如:安卓应用程序,小程序,iOS应用程序,Windows,Mac,L…

机器学习 监督学习论文_NeurIPS 2020最佳机器学习论文奖

NeurIPS 会议在提交的大量论文和1903年被接受的论文中,有3篇被授予。今年获奖的论文有:语言模型是学习者很少广义形式相关平衡的无悔学习动力学列子集选择和Nystrom方法的改进保证和多重下降曲线该NeurIPS委员会由一些指导准则。最好的论文必须具有革命性…

iis 装完framework4 7 无法切换_扫盲贴之电压并列与电压切换

点击上方电气小青年,关注并星标由于微信改版,只有星标才能及时看到我们的消息哦━━━━━━推荐阅读:《国内电气顶尖高校的奖学金介绍,总奖学金接近150万!》《世界工业自动化公司行业前十名:西门子、ABB、…

unixbench类似_UnixBench的实现介绍-阿里云开发者社区

很多用户都用UnixBench做性能测试,并做厂商之间的对比,那UnixBench到底做了哪些性能测试,本篇从代码层面阐述UnixBench做了哪些测试。在细说UnixBench的实现之前,先放一个总结果UnixBench算分介绍有类似结果,然后一个个…

android 集成同一interface不同泛型_C# 基础知识系列- 10 反射和泛型(二)

0. 前言 这篇文章延续《C# 基础知识系列- 5 反射和泛型》,继续介绍C#在反射所开发的功能和做的努力。上一篇文章大概介绍了一下泛型和反射的一些基本内容,主要是通过获取对象的类型,然后通过这个类型对象操作对象。这一篇介绍一个在反射中很重…

hdu 1297 递推难题

这题的话,我能玩一年 今天做了很多递推的题,这题无疑是最复杂的 其实可以看出来,2,3,4,5为一类,不妨定义为2型,1,6为一类,定义为1型 规定num[i]为结尾是i的凹槽的数量 我们可以能轻易的推出 sum num[1]*2n…

mysql 8.0远程连接_安装mysql 8.0.17并配置远程访问的方法

一、安装前准备查看数据库版本命令: mysql --versionmysql-community-common-8.0.17-1.el7.x86_64.rpmmysql-community-libs-8.0.17-1.el7.x86_64.rpmmysql-community-client-8.0.17-1.el7.x86_64.rpmmysql-community-server-8.0.17-1.el7.x86_64.rpm二、安装RPM包依…

python体育竞技分析代码200行_使用Python进行体育竞技分析(预测球队成绩)

使用Python进行体育竞技分析(预测球队成绩) 发布时间:2020-09-18 06:38:27 来源:脚本之家 阅读:69 今天我们用python进行体育竞技分析,预测球队成绩 一. 体育竞技分析的IPO模式 : 输入I(input)&…

为什么有时优盘是只读模式_JS专题之严格模式

ECMAScript 5 引入了 strict mode ,现在已经被大多浏览器实现(从IE10开始)一、什么是严格模式顾名思义,JavaScript 严格模式就是让 JS 代码以更严格的模式执行,不允许可能会引发错误的代码执行。在正常模式下静默失败的代码&#…

mysql router 介绍_MySQL Router 介绍篇

MySQL Router 是什么?相信还有很多人没有听说过MySQL Router,很多人对它还不了解,在这篇文章里,将对MySQL Router进行一个简明介绍。首先,介绍一下MySQL Router推出的背景。MySQL Router 是一个轻量级的中间件&#xf…

react 更新input 默认值setfieldsvalue_值得收藏的React面试题

react1、什么是虚拟DOM?难度: ⭐虚拟 DOM (VDOM)是真实 DOM 在内存中的表示。UI 的表示形式保存在内存中,并与实际的 DOM 同步。这是一个发生在渲染函数被调用和元素在屏幕上显示之间的步骤,整个过程被称为调和。2、类组件和函数组件之间的区…

实验二Step1-有序顺序表

1 #include<stdio.h>2 3 struct job4 {5 char name[10];//作业名称6 char status;//当前状态7 int arrtime;//到达时间8 int reqtime;//要求服务时间9 int startime;//调度时间 10 int finitme;//完成时间 11 float TAtime,TAWtime;//周转时…

ocx控件 postmessage消息会消失_APP控件之二——弹框

弹框分为两种&#xff1a;模态弹框和非模态弹框一、模态弹框模态弹框和非模态弹框最大的区别就是是否强制用户交互。模态弹框会打断用户的当前操作流程&#xff0c;用户不在弹框上操作的话&#xff0c;其余功能都使用不了。优点是&#xff1a;可以很好的获取的用户的视觉焦点缺…

结对编程(1)

我的结对编程项目搭档是王以正&#xff0c;我们的代码也是基于他个人项目的代码修改的。 由于王以正同学不在宿舍住也不怎么会宿舍&#xff0c;我们结对编程的时间较少&#xff0c;不过他将他的代码代码放到了github上面&#xff0c;这也让我有机会学习了github的使用。感觉这个…

伪代码block转换成程序流程图_程序设计基础

1、程序与程序设计语言的基本知识1&#xff09;程序&#xff1a;为解决某一问题而采用程序设计语言编写的一个指令集合。程序算法&#xff08;对操作的描述&#xff09;数据结构&#xff08;对数据的描述&#xff09;程序设计语言语言工具和环境。2&#xff09;程序的特点&…

mysql 内联函数_C++之内联函数

C继承C的一个重要特性是效率&#xff0c;在C中保护效率的一个方法是使用宏(macro),宏的实现是使用预处理器而不是编译器&#xff0c;预处理器直接用宏代码替换宏调用&#xff0c;所以就没有了参数压栈、生成汇编语言的CALL、返回参数、执行汇编语言的RETURN的时间花费&#xff…

10桌面管理文件收纳_二十余件精选桌面好物推荐,让学习工作生活满满正能量!...

这些提升办公桌幸福感的好物&#xff0c;能让你的学习与工作正能量满满&#xff01;01 笔记本支架笔记本是为了人们出行方便而设计的&#xff0c;显示器的位置并没有照顾到长期对着显示器码字的人群&#xff0c;许多以笔记本为主力的办公族也被迫成为了低头族。绿巨能(llano)笔…

hash 值重复_面试题:HashSet是如何保证元素不重复的

面试官&#xff1a;你能简单介绍List和Set有什么区别吗&#xff1f;小憨&#xff1a;List是一个有序的集合&#xff0c;在内存是连续存储的&#xff0c;可以存储重复的元素&#xff0c;List查询快&#xff0c;增删慢&#xff1b;Set是一个无序的集合&#xff0c;在内存中不连续…

RabbitMQ 原文译03--发布和订阅

发布/订阅 在之前的案例中我们创建了一个工作队列,这个工作队列的实现思想就是一个把每一个任务平均分配给每一个执行者,在这个篇文章我们会做一些不一样的东西,把一个消息发送给多个消费者,这种模式就被称作"发布/订阅". 为了说明这个模式,我们将要创建一个简单的日…