什么是缓存?

缓存就是数据交换的缓冲区,用于临时存储数据(使用频繁的数据)。当用户请求数据时,首先在缓存中寻找,如果找到了则直接返回。如果找不到,则去数据库中查找。缓存的本质就是用空间换时间,牺牲数据的实时性,从而减轻数据库压力,尽可能提高吞吐量,有效提升响应速度。

缓存的分类

缓存的应用范围十分广泛,常见的有文件缓存、浏览器缓存、数据库缓存等等。但今天我们着重关注的是 WEB 应用服务领域,根据缓存与应用的耦合度,可以分为本地缓存和分布式缓存:

  • 本地缓存

    指在应用中的缓存组件,最大的优点是应用和缓存是在同一个进程内部,请求缓存速度快;同时,它的缺点也是因为缓存跟应用程序耦合,多个应用程序无法直接共享缓存,各应用或集群的各节点都需要维护自己的单独缓存

  • 分布式缓存

    指的是与应用分离的缓存组件或服务,最大的优点是自身就是一个独立的应用,与本地应用隔离,多个应用可直接共享缓存

缓存的特点

缓存也是一个数据模型对象,那么必然有它的一些特征:

  • 命中率

    命中率 = 返回正确结果数 / 请求缓存次数,命中率问题是缓存中的一个非常重要的问题,它是衡量缓存有效性的重要指标。命中率越高,表明缓存的使用率越高。

  • 最大元素

    缓存中可以存放的最大元素的数量,一旦缓存中元素数量超过这个值,将会触发缓存清空策略。根据不同的场景合理设置最大元素值,可以在一定程度上提高缓存的命中率,从而更有效的利用缓存。

缓存清空策略

缓存的存储空间有限制,当缓存空间被用满时,就需要缓存清空策略来处理。常见的一般策略有:

  • 先进先出策略

    先进入缓存的数据,在缓存空间不足时会被优先被清理掉,以腾出新的空间接受新的数据。先进先出策略主要比较缓存元素的创建时间,在数据实效性要求较高的场景下可选择该类策略,优先保障最新数据可用

  • 最少使用策略

    无论是否过期,根据元素被使用的次数判断,清除使用次数较少的元素。最少使用策略主要比较元素的命中次数,在保证高频数据有效性场景下,可选择该类策略

  • 最近最少使用策略

    无论是否过期,根据元素最后一次被使用的时间戳,清除最远使用时间戳的元素。策略算法主要比较元素最近一次被使用的时间。适用于热点数据场景,优先保证热点数据的有效性

此外,还有一些简单策略,比如:

  • 根据过期时间判断,清理过期时间最长的元素
  • 根据过期时间判断,清理最近要过期的元素
  • 随机清理
  • 根据关键字(或元素内容)清理等等

Redis 实现分布式缓存

可以利用 Mybatis 自带的本地缓存,结合 Redis 实现分布式缓存。主要思路是将 Mybatis 二级缓存的存放地点从本地改为配置了 Redis 的远程服务器。

第一步,创建一个 SpringBoot 工程,整合 MyBatis 和 Redis,在 Mapper 文件中加入 <cache/> 标签开启二级缓存。

<cache/> 标签默认采用 PrepetualCache,该类是 Cache 接口的实现类,维护一个 Map 来保存数据。我们要作改造,就要自定义一个实现类并替换 <cache type="xxxx.RedisCache">

实现自定义 RedisCache

public class RedisCache implements Cache {

  	// 当前放入缓存的 mapper 的 namespace,也是缓存的唯一标识
private final String id; public RedisCache(String id) {
System.out.println("id:" + id);
this.id = id;
} /**
* 返回 cache 的唯一标识
*/
@Override
public String getId() {
return this.id;
} /**
* 缓存放入值
*/
@Override
public void putObject(Object key, Object value) {
System.out.println("放入缓存");
// 通过工具类获取 redisTemplate
RedisTemplate redisTemplate = getRedisTemplate();
// 使用 redishash 类型作为缓存存储模型
redisTemplate.opsForHash().put(id.toString(), key.toString(), value);
} /**
* 获取缓存中的值
*/
@Override
public Object getObject(Object key) {
System.out.println("获得缓存");
// 通过工具类获取 redisTemplate
RedisTemplate redisTemplate = getRedisTemplate();
// 根据 key 从 redis 的 hash 类型中获取数据
return redisTemplate.opsForHash().get(id.toString(), key.toString()); } /**
* 根据指定的 key 删除缓存
* 该方法为 mybatis 保留方法,默认没有实现
*/
@Override
public Object removeObject(Object key) {
System.out.println("根据指定的 key 删除缓存");
return null;
} @Override
public void clear() {
System.out.println("清空缓存");
// 通过工具类获取 redisTemplate
RedisTemplate redisTemplate = getRedisTemplate();
// 清空 namespace
redisTemplate.delete(id.toString());
} /**
* 计算缓存数量
*/
@Override
public int getSize() {
RedisTemplate redisTemplate = getRedisTemplate();
return redisTemplate.opsForHash().size(id.toString()).intValue();
} /**
* 获取 redisTemplate
*/
private RedisTemplate getRedisTemplate() {
RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate");
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
return redisTemplate;
}
}

到此为止,使用 Redis 实现分布式缓存的目标就完成了。还有一点要注意的是,涉及到多表查询时,结果会包含另一个表的对象信息。由于 Mybatis 二级缓存清理只会清理自身 namespace 的缓存,所以被包含的对象信息不会被清理。如果此时表信息发生改变,将导致数据不一致。解决办法是每个 namespace 都使用同一个缓存

<!-- 共享其他 namespace 的缓存 -->
<cache-ref namespace="..."/>

缓存穿透(击穿)

客户端查询了一个数据库中没有的数据,导致缓存在这种情况下无法利用(数据库都没有则缓存更不可能有了)。此情况下可绕过缓存直接攻击数据库。

对于这种恶意访问,一种思路是先做校验,对恶意数据直接过滤掉,不要发送至数据库层;第二种思路是缓存空结果,就是对查询不存在的数据也记录在缓存中,这样就可以有效的减少查询数据库的次数。MyBatis 正是使用了第二种方式。

缓存雪崩

在系统运行的某一时刻,缓存全部失效,恰好这一时刻涌来大量客户端请求,导致数据库阻塞或挂起。导致缓存失效的原因有很多,常见的是缓存到了失效时间(所有的缓存设置了同样的过期时间),而没有作合适的处理。

要解决这个问题,一种方式是设置缓存永久存储(不推荐),另一种方式是针对不同业务数据设置不同的超时时间,防止集体失效。

WEB 应用缓存解析以及使用 Redis 实现分布式缓存的更多相关文章

  1. 在AspNetCore 中 使用Redis实现分布式缓存

    AspNetCore 使用Redis实现分布式缓存 上一篇讲到了,Core的内置缓存:IMemoryCache,以及缓存的基础概念.本篇会进行一些概念上的补充. 本篇我们记录的内容是怎么在Core中使 ...

  2. 【转载】在AspNetCore 中 使用Redis实现分布式缓存

    原文地址:https://www.cnblogs.com/szlblog/p/9045209.html AspNetCore 使用Redis实现分布式缓存 上一篇讲到了,Core的内置缓存:IMemo ...

  3. ASP.NET Core 使用 Redis 实现分布式缓存:Docker、IDistributedCache、StackExchangeRedis

    ASP.NET Core 使用 Redis 实现分布式缓存:Docker.IDistributedCache.StackExchangeRedis 前提:一台 Linux 服务器.已安装 Docker ...

  4. Redis实现分布式缓存

    Redis 分布式缓存实现(一) 1. 什么是缓存(Cache) 定义:就是计算机内存中的一段数据: 2. 内存中数据特点 a. 读写快    b. 断电立即丢失 3. 缓存解决了什么问题? a. 提 ...

  5. Redis-基本概念、java操作redis、springboot整合redis,分布式缓存,分布式session管理等

    NoSQL的引言 Redis数据库相关指令 Redis持久化相关机制 SpringBoot操作Redis Redis分布式缓存实现 Resis中主从复制架构和哨兵机制 Redis集群搭建 Redis实 ...

  6. springboot+mybatis+redis实现分布式缓存

    大家都知道springboot项目都是微服务部署,A服务和B服务分开部署,那么它们如何更新或者获取共有模块的缓存数据,或者给A服务做分布式集群负载,如何确保A服务的所有集群都能同步公共模块的缓存数据, ...

  7. ASP.NET结合Redis实现分布式缓存

    最近一个项目ASP.NET+MySQL 有的网页打开初始化的查询需要10秒甚至更久,用户体验极差,而且并发量变大的时候网站容易崩溃 后来想了两种解决方案都不是太满意 1.数据库里建一张缓存表,后台作业 ...

  8. 在AspNetCore 中 使用Redis实现分布式缓存 (转载)

    文章概念描述 分布式缓存描述:分布式缓存重点是在分布式上,相信大家接触过的分布式有很多中,像分布式开发,分布式部署,分布式锁.事物.系统 等有很多.使我们对分布式本身就有一个很明确的认识,分布式就是有 ...

  9. ASP.Net Core 使用Redis实现分布式缓存

    本篇我们记录的内容是怎么在Core中使用Redis 和 SQL Server 实现分布式缓存. 一.文章概念描述   分布式缓存描述: 分布式缓存重点是在分布式上,相信大家接触过的分布式有很多中,像分 ...

随机推荐

  1. 什么?Java9这些史诗级更新你都不知道?Java9特性一文打尽!

    「MoreThanJava」 宣扬的是 「学习,不止 CODE」,本系列 Java 基础教程是自己在结合各方面的知识之后,对 Java 基础的一个总回顾,旨在 「帮助新朋友快速高质量的学习」. 当然 ...

  2. Open vSwitch with DPDK

    http://docs.openvswitch.org/en/latest/intro/install/dpdk/

  3. 四则运算生成命令行程序 (Python)

    Github项目地址:Github Pages 结对项目成员:张鹏 3118004985 郑靓 3118004988 一.项目需求分析 二.功能实现 三.代码实现or功能说明 ★ GUI功能扩展说明 ...

  4. muduo源码解析11-logger类

    logger: class logger { }; 在说这个logger类之前,先看1个关键的内部类 Impl private: //logger内部数据实现类Impl,内部含有以下成员变量 //时间 ...

  5. Spring是如何解决循环依赖的

    前言 在面试的时候这两年有一个非常高频的关于spring的问题,那就是spring是如何解决循环依赖的.这个问题听着就是轻描淡写的一句话,其实考察的内容还是非常多的,主要还是考察的应聘者有没有研究过s ...

  6. 解决SpringBoot jar包中的文件读取问题

    前言 SpringBoot微服务已成为业界主流,从开发到部署都非常省时省力,但是最近小明开发时遇到一个问题:在代码中读取资源文件(比如word文档.导出模版等),本地开发时可以正常读取 ,但是,当我们 ...

  7. 小案例-WebSocket实现简易聊天室

    前言 在详解 HTTP系列之一讲到HTTP/2.0 突破了传统的"请求-问答模式"这一局限,实现了服务器主动向客户端传送数据.而本章将通过一种在单个TCP连接上进行全双工通信的协议 ...

  8. javascript 数据结构与算法---二叉数

    二叉树,首先了解一些关于二叉数的概念(来自百度百科) 1. 二叉树(Binary tree)是树形结构的一个重要类型 2. 定义: 二叉树(binary tree)是指树中节点的度不大于2的有序树,它 ...

  9. .net core国际化

    1.背景 公司业务遍及全球各地,对应业务系统国际化就是顺理成章的事情.最近就接手了一批新老系统的国际化任务,这里把一些探索经验.案例记录下来.本身改造和探索过程包括.NET MVC的,以及.NET C ...

  10. Javascript基本数据认识

    1.Js的组成 2.Js的三种引入 行内式 <input type="button" value="来嘛来嘛" onclick="alert(' ...