一文学会如何使用缓存(一)

2 篇文章 0 订阅
1 篇文章 0 订阅

那么,缓存?说的是什么呢?

缓存,就是将一些需要读取数据放在磁盘或者内存中,但大部分为了追求速度,一般都放在内存中。

在读取数据的时候,一般是从关系型数据库中读取数据,在数据库层面也可以进行各种优化,例如读性能不足,这样可以添加几个从库,实现数据库的一主多从;例如写性能不足,那么可以分库分表

在有些场景中,要使用缓存,是因为无法解决读的速度,例如count(*)的操作,无论从数据库的层面如何优化,都不可能提高;还有一种就是sql的执行本身就必须消耗很多资源和时间。

例如各种关联查询子查询,这些时候,都可以将这些数据放在缓存当中,从而大大的减轻数据库的压力。

01、为什么使用缓存

缓存是能够最快提高服务响应速度的优化,没有之一。

如上图的应用程序直接连接数据库进行查询操作,假如有一个操作过来需要500ms,并且每一次查询都需要经过数据库,性能非常低,并且对于数据库并发量支持非常不友好,如果并发量太多导致数据库压力太大可能导致数据库崩溃或者卡死。

如上图,如果数据库崩溃,则依赖于数据库的其他应用都会无法运行

高性能

这个是大部分使用缓存的目的,能够最快以非常高的效率提高应用的性能

假设遇到一些查询速度很慢,比如权限,查询速度很慢,并且查询出来后很少发生变化,这种情况下大量查询对数据压力很大,并且性能不高。

我们将缓存中的key保存到缓存中,然后在需要查询的时候直接查询缓存,而不走数据库,这样响应数据非常快,并且对于数据库的压力很小。

一般缓存的查询都在微秒级,分布式缓存Redis中查询数据也在1ms中可以查询出来,这样在系统架构不进行大的变化的情况下完成了500倍的性能提升。

所以对于一些需要复杂操作耗时查出来的结果,确定后面不怎么变化,但是有很多读请求,直接将查询出来的结果放在缓存中,后面直接读缓存就好。

高并发

mysql 数据库对于高并发来说天然支持不好,mysql 单机支撑到 2000QPS 也开始容易报警了

所以若是系统高峰期一秒钟有1万个请求,那么一个 mysql 单机绝对会死掉,这个时候就只能上缓存,把很多数据放入缓存,别放入 mysql。

缓存功能简单,说白了就是 key-value 式操作,单机支撑的并发量一秒可达几万十几万,单机承载并发量是 mysql 单机的几十倍。

02、收益与成本

收益

通过缓存加速读写速度:在内存中读写比硬盘速度快

降低数据库服务器的负载:比如业务端的请求的数据大多数都由Redis服务器来处理,大大减轻MySQL服务器的压力

成本

数据不一致问题:比如Redis服务器与数据库服务器之间的某些数据可能会发生不一致问题,这是由两个服务器的数据更新策略不同引起的

代码维护成本:需要添加数据缓存的逻辑代码

运维成本:比如需要维护RedisCluster

03、缓存分类之系统划分

应用级缓存

应用级缓存也就是我们平时写的应用程序中所使用的缓存

在平时程序中一般是按照如下操作流程来实现缓存的操作,首先张三用户读取数据库,并将读取的数据存入到缓存中,其他用户读取的时候,直接从缓存中读取,而不用查询数据库,从而提高程序的执行速度和效率。

系统级别缓存

系统级别缓存是抛开我们应用程序之外硬件的缓存操作,例如某些CPU的缓存操作和如下图多级缓存流程类似。

CPU在操作数据的时候,先读取1级缓存,1级缓存如果没有数据则读取2级缓存,2级缓存没有数据则读取3级缓 存,3级缓存如果没有数据就直接从主存储器(存储指令和数据)读取数据。

04、缓存分类之设计划分

本地缓存

直接运行在应用程序本地的缓存组件, 比如 JVM 中的 Map 数据结构,可以作为最简单的数据缓存。

如果你的应用程序只需要运行在一台服务器上,并且多个应用程序之间不需要共享缓存的数据(比如用户 token),可以直接采用本地缓存,访问缓存时不需要通过网络传输,非常地方便迅速。

但是本地缓存会和你的应用程序强耦合,应用程序停止,本地缓存也就停止了,而且如果是在分布式场景下,多个机器都要使用缓存,此时如果在每个服务器上单独维护一份本地缓存,不仅无法共享数据,而且非常浪费内存(因为每台机器可能缓存了相同的数据)。

分布式缓存

分布式缓存是指独立的缓存服务,不和任何一个具体的应用耦合,可以独立运行并搭建缓存集群。

类似数据库,所有的应用程序都可以连接同一个缓存服务以获取相同的缓存数据。

除了数据共享外,分布式缓存的优点还有很多,比如不需要每台机器单独维护缓存、可以集中管理缓存和整体管控分析、便于扩展和容错等,但是应用必须要通过网络访问分布式缓存服务,会产生额外的网络电销成本

并且每台机器都有可能会对整个分布式缓存服务产生影响,而一旦分布式缓存挂了,所有的应用都可能出现瘫痪(缓存雪崩)。

多级缓存

上述两种缓存没有绝对的优劣,要根据实际的业务场景进行选型。

其实还可以将本地缓存与分布式缓存相结合,形成多级缓存服务,架构如下:

当首次查询时(不存在缓存),会同时将数据写入本地缓存和分布式缓存,之后的查询优先查询分布式缓存,而如果分布式缓存宕机,则从本地缓存获取数据,通过多级缓存机制,能够起到兜底的作用,即使缓存挂掉,也能支撑应用运行一段时间。

05、缓存淘汰策略

使用了缓存后,缓存的容量是有限的,如果缓存满了之后如何淘汰一些数据呢?

刚刚提到的 Map 数据结构是一个思路,但是和我们自己的电脑存储文件、或者是和 JVM 存储对象一样,内存当然不是无限的。

因此在实现缓存时,必须要设计一套缓存淘汰策略,按照某种机制回收缓存占用的内存,保证缓存数据不会无限地增长直到撑爆内存。

LRU

近期少使用

LRU(Least Recently Used)是最经典的内存淘汰策略,其设计原则是 “如果一个数据在最近一段时间没有访问到,那么在将来它被访问的可能性也很小”。

 即根据数据的最近访问时间来进行淘汰,缺点是可能会由于一次冷数据的批量查询而误删除大量的热点数据。

近似 LRU 算法

类似 LRU 算法,只是每次随机选择一批数据进行 LRU 淘汰,而不是全量 LRU 运算,牺牲部分准确度,以提升算法执行效率。

Redis 3.0 之后对其进行了优化,维护了一个侯选池,将随机选择的数据放入侯选池中进行 LRU 运算。当侯选池放满后,新随机的数据会替换掉池中最近被访问的数据。

TTL

超时时间

TTL(Time To Live)是指用户为缓存设置的过期时间,当前时间到达过期时间时,将删除缓存;如果缓存空间已满,则优先淘汰最接近过期时间的数据。

LFU

最近最不经常使用

LFU(Least Frequently Used)策略会记录每个缓存数据的最近访问次数(频率),并优先清除使用次数较少的数据。这种算法存在的显著缺点是,最新写入的数据由于访问次数少,常常刚被缓存就删除了。

FIFO

先进先出

FIFO(First In First Out)先进先出策略会将数据按照写入缓存的顺序进行排队,当缓存空间不足时,最先进入缓存的数据会被优先删除。是一种比较死板的策略,不考虑数据热度,可能会淘汰大量的热点数据,但是实现起来相对容易。

Random

随机淘汰策略,没啥好说的,一般不建议使用

06、缓存应用场景

下面我们分析下缓存使用的一些场景

频繁查询数据缓存

有一些数据经常被访问,而且变更频率较低,实时性要求不高的数据,可以把它存储到缓存中,每次读取数据直接读缓存即可,从而提升数据的加载速度和系统的性能。

列表排序分页数据

一些变更频率较低查询频次较高的列表、分页、排序数据,可以存入到Redis缓存,每次查询分页或者排序的时 候,直接从Redis缓存中获取

计数器

网站中用于统计访问频次、在线人数、商品抢购次数等,也可以使用缓存来实现。

详情内容

站点中,资讯内容、商品详情等较大变更频率又低的内容,可以采用缓存来加速数据的读取,降低IO操作。

分布式Session

实现会话共享的时候,可以使用Session来存储需要共享的会话,从而节省内存空间。

热点排名

我们可以使用ZSet来存储热数据,并实现热点数据的排名

发布订阅

用Redis也可以实现发布与订阅,但不推荐,推荐用MQ。

分布式锁

可以使用Redisson结合Redis实现分布式锁,Redis实现的分布式锁效率极高,得到了市场的广泛使用

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值