一、缓存的基本概念
缓存是一种数据存储技术,其基本概念包括以下要点:
①数据存储: 缓存存储数据以供后续快速访问,通常存储在内存中,因为内存的读取速度比磁盘快得多。
②缓存命中: 当请求的数据在缓存中已经存在时,称为缓存命中,可以立即返回数据,减少对后端存储的访问。
③缓存失效: 缓存数据可能会过期或失效,需要定期更新以保持数据的新鲜性。
④缓存淘汰: 当缓存空间不足时,系统需要选择哪些数据从缓存中淘汰出去,以便为新数据腾出空间。
二、Redis缓存的工作流程
Redis作为一款内存数据库,其工作流程如下:
①数据写入Redis服务器:当应用程序生成或获取数据时,将其写入Redis服务器。这可以是各种类型的数据,包括数据库查询结果、计算得出的结果、频繁使用的配置信息等。Redis的快速写入特性和支持多种数据结构的优势使其成为理想的缓存存储引擎。
②检查Redis缓存中是否存在数据:在应用程序需要访问数据之前,首先检查Redis缓存中是否已经存在所需的数据。这一步是通过向Redis发起查询请求,检查指定的键是否存在于缓存中。这样的检查能够快速确定数据是否可在缓存中找到,从而决定后续的处理流程。
③缓存命中:如果检查发现数据已存在于Redis缓存中,即缓存命中,应用程序可以直接从缓存中获取数据,而无需访问后端存储系统。这带来的直接好处是极大地减少了对数据库等后端存储的访问次数,从而降低了系统的响应时间,并减轻了后端存储的负载。
④缓存未命中:如果检查发现数据不存在于Redis缓存中,即缓存未命中,应用程序则需要从后端存储系统(例如数据库)中获取数据。一旦获取到数据,应用程序会将数据存储到Redis缓存中,以备将来的访问。这样的操作确保了下一次相同的请求能够从缓存中快速获得数据,提高了系统的整体性能。
三、Redis缓存简单实现
本文以黑马点评项目为例,实现Redis缓存,本项目由SpringBoot框架整合,如下图所示,项目有以下四大组件,本文项目由单节点Mysql和单节点Redis构成。
①Nginx服务器:可以作为HTTP协议的负载均衡器,将请求分发到下游的Tomcat服务器,还可以充当静态资源服务器,有效承受大量并发请求。它还可以使用Lua脚本来绕过Tomcat,直接访问Redis,提高响应速度。
②Tomcat服务器:Tomcat充当Web应用服务器,执行业务逻辑和处理动态请求。它处理来自Nginx的请求,执行与业务相关的操作,然后将响应返回给Nginx,以供转发给客户端。
③MySQL数据库:MySQL作为关系型数据库,负责存储应用程序的数据。它可以处理复杂的查询和事务,并提供数据的持久性。
④Redis缓存:Redis用作缓存层,可以大大提高访问性能。Redis集群可以确保高可用性和可伸缩性,使Redis能够为外部服务提供高效的数据访问。
四、实现Redis缓存
(1)目标:通过Redis缓存Web应用的美食模块下的第一个商店信息,如下图所示,缓存该页面下的JSON文件内容,对应的URL为: http://localhost:8080/api/voucher/list/1
(2)在对应的后端项目中,找到对应的业务逻辑的显现类中添加代码,做到实现该界面JSON文件的缓存。
@Resource
private StringRedisTemplate stringRedisTemplate;
@Override
public Result queryById(Long id) {
//1.从redis查询商铺缓存
String key="cache:shop:" + id;
String shopJson = stringRedisTemplate.opsForValue().get(key);
//2.判断是否存在
if (StrUtil.isNotBlank(shopJson)) {
//3.存在,直接返回
Shop shop = JSONUtil.toBean(shopJson, Shop.class);
return Result.ok(shop);
}
//4.不存在,根据id查询数据库
Shop shop = getById(id);
//5.不存在,返回错误
if (shop==null) {
return Result.fail("店铺不存在");
}
//6.存在,写入redis
stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop));
//7.返回
return Result.ok(shop);
}
该代码的执行逻辑如下:
首先注入了一个名为stringRedisTemplate的String类型的Redis模板,用于与Redis进行交互。
然后查询缓存,构建了一个Redis缓存的键(key),格式为cache:shop:后跟商铺的ID,这里是cache:shop:1,然后尝试从Redis中查询该商铺的缓存数据。
再判断缓存是否存在,使用StrUtil.isNotBlank()方法检查商铺的缓存数据是否存在。以下有两种情况:
①如果Redis中存在与该键对应的缓存数据,则表示缓存命中,可以直接从缓存中返回商铺信息。
②如果缓存中不存在查询数据库: 如果缓存不存在(即缓存未命中),则会执行从数据库中查询商铺信息的操作,使用getById(id)方法根据商铺ID从数据库中获取商铺数据。 如果从数据库中查询也无法获取商铺数据,代码返回一个包含错误信息的Result对象,表示商铺不存在。如果成功从数据库中获取了商铺信息,代码将该商铺信息转换为JSON格式,并使用stringRedisTemplate将其写入Redis缓存中,以备将来使用。
(3)查看缓存的作用
当第一次访问该页面时,通过游览器开发者工具可以看到访问该网络包,如图,耗时431毫秒,如图所示,数据库的商店表执行了查询语句。
当第二次访问该页面时,通过游览器开发者工具可以看到访问该网络包,如图,耗时47毫秒,可以得出,客户端是从缓存数据库中得到的信息,因此使用的时间大大减少。
如图所示,数据库的商店表没有执行了查询语句,再次说明了是从Redis数据库中获取的信息。
通过以上案例初步实现了标准的Redis缓存技术,即查询数据库之前先查询缓存,如果缓存数据存在,则直接从缓存中返回,如果缓存数据不存在,再查询数据库,然后将数据存入redis。