目录
1、缓存能做什么?
当遇到页面打开很慢的时候,会引入缓存,这样页面响应就会块很多。快慢是相对的,从技术的角度来源,缓存的数据之所以快,是因为缓存是基于内存建立的,而内存的读写速度比硬盘快很多倍,所以用内存代替磁盘作为读写的介质能大大提高访问数据的速度。
访问过程如下:
通过在内存中存储被访问的数据提供给程序使用,从此达到提速的目的。
缓存有两个运用方式:预读取和延迟写。
- 预读取是在数据出口加缓冲区。
- 延迟写是在数据入口加缓冲区。
2、预读取
预读取就是先读取将要载入的数据,也称为“预存预热”,它是系统启动时先将硬盘中的一部分数据加载到内存中,供外部服务调用。如下图:
优点:因为系统启动之后就要面临巨大的请求量时,如果直接让这些请求到达数据库上,数据库的压力暴增,数据库服务容易崩溃,无法正常响应。而预读取就会解决这个问题,降低数据库的压力。
3、延迟写
数据库写入速度慢于读取速度,因为写入的时候有一系列的数据准确性的机制。要想提升写入速度,要么分库分表,要么通过缓存缓冲,然后再批量写入磁盘。
由于分库分表对跨表操作以及多条件组合查询的副作用大,所以他的复杂度大于引入缓存。
通过缓存机制加速写的过程称为“延迟写”,它预先将需要写入磁盘或数据库的数据,先暂时写入内存,然后返回成功,再定时将内存中的数据批量写入磁盘。
数据写缓存,有数据丢失的风险,所以延迟写一般仅用于对数据完整性要求不是很苛刻的场景,比如点赞数,参与用户数等,可以大大缓解对数据库的频繁修改所带来的压力。
在分布式缓存Redis中,其默认运用的持久化机制--RDB,就是这个思路。
4、哪里可以加缓存?
哪些数据需要添加到缓存?
两个标准衡量:
- 热点数据:被高频访问
- 静态数据:很少变化,读大于写
每个设立点可以挡掉一些流量,最终形成一个漏斗状的拦截效果,以此保护最后面的系统以及最终的数据库
5、缓存类别
- 浏览器缓存
数据被缓存在用户的浏览上,一般用于缓存图片,js与css这些资源,可以通过http消息头中的Cache-Control来控制它。
缺点:浏览器缓存是在用户端的缓存点,我们对它的控制力比较差,
- CDN缓存
将数据分发到CDN服务的服务商在各个地方提供的服务器上作为缓存,让用户访问就近的服务器上的缓存数据,就起到了压力分摊和页面访问加速效果。
缺点:节点众多,更新缓存数据比较慢,一般是分钟级别,所以一般适用不经常变动的静态数据。
- 网关缓存
在源站前面加一层网关,为的是加强安全机制或则统一分流策略的入口。
网关也可以作为缓存使用,网关与业务无关,它能够拦截请求,对背后的源站资源也有很大的受益,减少大量的CPU运算。
常用的网关缓存有Varnish、Squid与Ngnix。一般情况下,简单的缓存运用场景,用Ngnix即可,因为大部分时候我们会用它做负载均衡,能少引入一个技术就少一分复杂度。如果是大量的小文件可以使用Varnish,而 Squid 则相对大而全,运用成本也更高一些。
- 进程内缓存
进程内使用缓存是一个比较常见的场景。
一个请求到了这里说明它是和业务相关的,需要经过业务逻辑处理的。因此,这里使用缓存的成本大于前的3种,因为对缓存数据和数据库中的数据的一致性要求更高。
- 进程外缓存
进程外缓存是使用第三方工具,redis,memcached等等之类。
Redis 和 Memcached 该怎么选择的思路
对资源(CPU、内存等)利用率格外重视的话可以使用Memcached ,但程序在使用的时候需要容忍可能发生的数据丢失,因为纯内存的机制。如果无法容忍这单,并对资源利用率也比较豪放的话就可以使用Redis。而且Redis的数据库结构更多, Memcached 只有 key-value,更像是一个 NoSQL 存储。
- 数据库缓存
数据库缓存是数据库内部机制,一般都会设置缓存空间大小。
6、缓存引发的问题
1、缓存雪崩
大量的请求并发进入时,由于某些原因未起到预期的缓存效果,在很短的时间内,导致请求全部转移到数据库,造成数据库压力过大,数据库崩溃。
解决方案:通过“加锁排队”或者“缓存过期时间增加随机值”
2、缓存穿透
和缓存雪崩类似,区别是持续更长时间,因为每次“cache miss”后,依然无法从数据源加载数据到缓存,导致持续产生“cache miss”
解决方法:通过“布隆过滤器”或者“缓存空对象”
3、缓存并发
一个缓存key下的数据被同时set,怎么保证业务的准确性?再加上数据库的话?进程内缓存、进程外缓存与数据库三者皆用的情况下呢?
解决方法:使用“先DB再缓存”的方式,并且缓存操作使用delete而不是set
4、缓存无底洞
分布式缓存是可以无限横向扩展,不是节点越多越好
解决方案:缓存符合“边际效用递减”
5、缓存淘汰
内存总是有限的,如果数据量很大,那么根据具体的场景定制合理的淘汰策略是必不可少的, 如 LRU、LFU 与 FIFO 等等。