高性能章节:转自公众号:数据库开发:
https://mp.weixin.qq.com/s?__biz=MzI3NDA4OTk1OQ==&mid=2649902899&idx=2&sn=fb77a500d013cbdd91e7f8391365a100&chksm=f31fbdbbc46834adeb2f57ff4c468bf95a38e2327babd9d188cb54ec98c68a9e1d3a898dee51&mpshare=1&scene=23&srcid=&sharer_sharetime=1567180359832&sharer_shareid=49e276b2e624f54fc5b024238a201770#rd
大家普遍的理解就是当我们遇到某个页面打开很慢的时候,会想到引入缓存,
这样页面打开就快了。
其实快和慢是相对的,从是技术角度来说,缓存之所以快是因为缓存是基于内存建立的,而内存的读写速度比硬盘快X倍,所以用内存来代替硬盘作为读写的介质自然能大大提高访问数据的速度.
这个过程大致是这样的,通过在内存中存储被访问过的数据供后续访问时使用,以此来达到提速的效果.
除此之外,缓存还有另外2个重要的运用方式:【预读取】和【延迟写】
【预读取】:
预读取就是预先读取将要载入的数据,也可以称作【缓存预热】。就是在系统对外提供服务之前,
先将硬盘中的一部分数据加载到内存中,然后再对外提供服务。
为什么要这样做呢? 因为系统一旦启动就要面临成千上万的请求进来,如果直接让这些请求达到数据库上,非常大的数据库压力暴增,直接被干趴,无法正常响应.
为了缓解这个问题,需要通过【预读取】来解决.
可能你会问,哪怕用了缓存还是坑不住呢?那就是做横向扩展+负载均衡的时候到了。
如果说【预读取】是在【数据出口】加了一道前置的缓冲区的话,那么故名思义,下面要说的
【延迟写】就是在【数据入门】后面加了一道后置的缓冲区。
【延迟写】
你可能知道,数据库的写入速度是慢于读取速度的,因为写入的时候有一系列的保证数据准确性的
机制。
所以,如果要想提升写入速度的话,要么做分库分表,要么就是通过缓存来进行一道缓冲,再一次性批量写到磁盘,以此来提速。
【补充】:由于分库分表对跨表操作以及多条件组合查询的副作用巨大,所以引入它的复杂度远大运引入缓存,我们应当优先考虑引入缓存的方案。
那么,通过缓存机制来加速“写”的过程就可以称作【延迟写】。就是预先将需要写入到磁盘或者数据库的数据,先暂时写入到内存,然后就返回成功。再定时将内存中的数据批量写入到磁盘。
可能你会想,写到内存就认为成功,万一中途出现意外,断电,停机等导致程序异常终止的情况,数据不就丢了吗?
是的,所以,【延迟写】一般仅用于对数据完整性要求不是那么苛刻的场景。比如点赞数,参与用户数等,可以大大缓解对数据库频繁修改所带来的压力。
其实在我们熟知的分布式缓存redis中,其默认运用的持久化机制--RDB,也是这样的思路。
在一个成熟的系统中,能够运用到缓存的地方其实并不是一处。下面就来梳理哪些地方可以“加缓存”。
【哪里可以加缓存?】
在说哪里可以加缓存之前,我们先搞清楚一个事情,我们要缓存什么?也就是符合什么特点的
数据才需要加缓存?毕竟加缓存是一个额外的成本投入,得物有所值。
一般来说你可以用两个标准来判断:热点(被高频访问,如几十次/秒以上)数据,静态(很少变化,读远大于写,如几天变更一次)数据。
接下来就可以替它们找到合适的地方加缓存了。
缓存的本质是一个”防御性“的机制,而系统之间的数据流转是一个有序的过程,所以选择在哪里加缓存就相当于选择在一条马路的哪个位置设路障。在这个路障之后的道路都能受到保护,不被车流碾压。
那么在以终端用户为起点,系统所用的数据库为终点的这条道路上可以作为缓存设立点的位置大致有以下这些。
每个设立点可以挡掉一些流量,最终形成一个漏斗状的拦截效果,以此保护最后面的系统及最终的数据库。
以下描述每一个应用场景及需要注意的点:
一:浏览器缓存
这是离用户最近的可以作为缓存的地方,而且借助的是用户”资源“(缓存的数据在用户的终端设备上),性价比可谓最好,让用户帮你分担压力。
当你打开浏览器的开发者工具,看到from cache或者from memory cache,from disk cache的时候,
就意味着这些数据已经被缓存在了用户的终端设备上了(没网络的时候也能访问到一部分内容就是这个原因)。
这个过程是浏览器替我们完成的,一般用于缓存图片,js,css这些。我们可以通过http消息头中的Cache-Control来控制它。
js里的全局变量,及cookie等运用也属于该范畴。
浏览器缓存是在用户侧的缓存点,所以我们对其的掌控力就差很多,在没有发起新请求的情况下,你无法主动去更新数据。
二:CDN缓存:
提供cdb服务的服务商,在全国甚至是全球部署着大量的服务器节点(可以叫做【边缘服务器】)。
那么将数据分发到这些遍布各地服务器上作为缓存,让用户访问就近的服务器上的缓存数据,就可以起到压力分摊和加速效果。这在Toc类型的系统上运用,效果格外显著。
但是需要注意的是,由于节点众多,更新缓存数据比较缓慢,一般至少是分钟级别。所以一般仅适用于不经常变动的静态数据。
【附】解决方式也是有的,就是在url后面带个自增数或者唯一标示,如?v=1001,因为不同的url会被视作”新“的数据和文件,被重新create出来。
三:网关(代理)缓存:
到这里做缓存就是在你自己的地盘了,很多时候我们会在源站前面架一层网关(或者说反向代理,正向代理),为的是做一些安全机制或者统一分流策略的入口。
同时这里也是做缓存的一个好场所。毕竟网关是”业务无关性“的,它能够拦下来的请求,对背后的
源站也是很大的收益,减少了大量的cpu运算。
常用的网关(代理)缓存有Varnish,Squid,Nginx。一般情况下,简单的缓存运用场景,用nginx即可,因为大部分时候我们会用它来做负载均衡,能少引入一个技术就少一分复杂度。如果是大量的小文件可以使用Varnish,而Squid相对大而全,运用成本也更高一些。
进程内缓存:
一个请求能走到这里说明它是"业务相关"的,需要经过业务逻辑运算。
也正因为如此,从这里开始对缓存的引入成本比前面3种大大增加,因为对缓存和数据库之间的
【数据一致性】要求更高了。
可能我们大多数程序员第一次刻意使用缓存的场景就是这个时候。进程内核进程外的缓存运用中有很多
的细节需要注意。
进程外缓存:
就是Redis,memcached之类,甚至也可以自己单独写一个程序来专门存放缓存数据,供其他程序远程调用。
同样,这里的细节不多说。这里先多说几句关于redis和memcached该怎么选择的建议。
对资源(cpu,内存等)利用率格外重视的话可以使用Memcached,但程序在使用的时候需要容忍可能发生的数据丢失,因为是纯内存的机制。如果无法容忍这点,并且对资源利用率也比较豪放的话可以使用redis。而且redis的数据库结构更多,memcached只有key value,更像是一个nosql存储。
四:数据库缓存
数据库本身自带缓存模块的,否则也不会叫内存杀手,基本上你给多少内存就能吃多少。
数据库缓存是数据库的内存机制,这里不深入了。一般都会设置缓存空间大小的配置来让你进行干预。
最后,磁盘本身也有缓存。所以,当你发现为了让数据能够平稳的写到物理磁盘中真的一波三折。
【缓存】是Siliver bullet吗?
可能你会想缓存那么好,那么应该多多益善,只要慢就上缓存来解决?
一个事物看上去再好,也有它负面的一面,缓存也有一系列的副作用需要考虑。除了上面提到的
【缓存更新】和【缓存与数据的一致性】问题,还有诸如:
1.缓存雪崩;
2.缓存穿透;
3.缓存并发;
4.缓存无底洞;
5.缓存淘汰
6。。。。
来源:博客园
作者:朕在coding
链接:https://www.cnblogs.com/newcapecjmc/p/11429713.html