Java的缓存

目录

MyBatis的缓存机制

一级缓存(SqlSession级别)

二级缓存(全局级别)

[Mapper 级别和 namespace 级别的关系](#Mapper 级别和 namespace 级别的关系)

Spring的缓存机制

[2.1 Redis 在 Spring 中的集成](#2.1 Redis 在 Spring 中的集成)

[2.2 Caffeine 在 Spring 中的集成](#2.2 Caffeine 在 Spring 中的集成)

[2.3EhCache 在 Spring 中的集成](#2.3EhCache 在 Spring 中的集成)

SpringBoot的缓存机制

[Redis 在 SpringBoot 中的集成](#Redis 在 SpringBoot 中的集成)

[1. 添加依赖](#1. 添加依赖)

[2. 配置 Redis](#2. 配置 Redis)

[3. 启用缓存](#3. 启用缓存)

[4. 配置 RedisTemplate(可选)](#4. 配置 RedisTemplate(可选))

[5. 编写 Redis 操作类](#5. 编写 Redis 操作类)

[6. 使用 Redis](#6. 使用 Redis)

[MYSQL 的缓存机制](#MYSQL 的缓存机制)

[1. 查询缓存(Query Cache)](#1. 查询缓存(Query Cache))

特点

配置

[2. InnoDB 缓冲池(Buffer Pool)](#2. InnoDB 缓冲池(Buffer Pool))

特点

配置

[3. MyISAM 缓存](#3. MyISAM 缓存)

特点

配置

[4. 二进制日志缓存(Binary Log Cache)](#4. 二进制日志缓存(Binary Log Cache))

特点

配置

[5. 临时表缓存(Temporary Table Cache)](#5. 临时表缓存(Temporary Table Cache))

特点

配置

[6. 表缓存(Table Cache)](#6. 表缓存(Table Cache))

特点

配置

mysql的缓存机制

[1. 查询缓存(Query Cache)](#1. 查询缓存(Query Cache))

[2. InnoDB 缓冲池(Buffer Pool)](#2. InnoDB 缓冲池(Buffer Pool))

[3. MyISAM 缓存](#3. MyISAM 缓存)

[4. 二进制日志缓存(Binary Log Cache)](#4. 二进制日志缓存(Binary Log Cache))

[5. 临时表缓存(Temporary Table Cache)](#5. 临时表缓存(Temporary Table Cache))

[6. 表缓存(Table Cache)](#6. 表缓存(Table Cache))

[Java 本身的缓存](#Java 本身的缓存)

[1. 常量池(Constant Pool)](#1. 常量池(Constant Pool))

[2. String.intern()](#2. String.intern())

[3. HashMap 或其他集合](#3. HashMap 或其他集合)

[4. WeakHashMap](#4. WeakHashMap)

[5. SoftReference 和 WeakReference](#5. SoftReference 和 WeakReference)

[6. java.util.concurrent 包](#6. java.util.concurrent 包)

[7. 设计模式:单例模式](#7. 设计模式:单例模式)

[8. Java 缓存库](#8. Java 缓存库)


先来看

MyBatis的缓存机制

MyBatis提供了两级缓存机制,一级缓存( SqlSession****级别)和二级缓存(全局级别),以通过减少数据库的查询次数来提高应用的性能。

一级缓存(SqlSession级别)

一级缓存是MyBatis默认开启且无法关闭的缓存,作用范围是SqlSession级别(SQL会话==SqlSession)

  • 作用范围 :仅限于同一个SqlSession实例。
  • 默认开启:无需任何配置。
  • 生命周期 :与SqlSession一致,SqlSession关闭时,一级缓存清空。
  • 失效条件
    • 执行insertupdatedelete操作时,一级缓存会被清空。
    • 调用SqlSession.clearCache()方法手动清空。
  • 存储位置 :默认存储在 JVM 内存中,通过 HashMap 实现

在同一个 SQL 会话中,对于相同的查询请求,第一次会从数据库中获取数据,并缓存结果,之后相同的查询请求将直接从缓存中获取数据,不会再去访问数据库。

java 复制代码
try (SqlSession session = sqlSessionFactory.openSession()) {
    UserMapper mapper = session.getMapper(UserMapper.class);
    User user1 = mapper.getUserById(1); // 查询数据库
    User user2 = mapper.getUserById(1); // 从一级缓存获取
    System.out.println(user1 == user2); // true
}

二级缓存(全局级别)

二级缓存是跨SQL 会话的共享缓存,作用范围是 Mapper****级别,需要手动配置。

开启二级缓存后,数据会存储在全局作用域内,这意味着,即使 SQL 会话关闭,缓存数据仍然可用,可以被其他 SQL 会话复用。

  • 作用范围 :同一个Mapper的多个SqlSession共享。
  • 手动配置 :默认关闭,需在mybatis-config.xmlMapper.xml中配置。
  • 缓存实现:默认使用内存缓存,也可集成Redis等外部缓存。
  • 失效条件 :在Mapper中执行insertupdatedelete操作时,二级缓存会被清空。
  • 存储位置
    • 默认存储在 JVM 内存中。
    • 可以通过配置集成外部缓存,如 EhCache 或 Redis。

也有的说二级缓存的作用范围是 namespace 级别的,但其实namespace 级别和Mapper级别这两种说法本质上是相同的,只是表述方式不同。

Mapper 级别和 namespace 级别的关系

  • Mapper级别:指的是同一个 Mapper接口或 Mapper XML 文件的作用范围。在 MyBatis 中,每个 Mapper 接口或 XML 文件都有一个唯一的 namespace
  • namespace级别:namespaceMapper 的唯一标识,通常对应一个 Mapper 接口或 XML 文件。二级缓存的作用范围是基于 namespace 的,这意味着同一个 namespace 下的所有操作共享同一个缓存。

所以,Mapper 级别和namespace级别在 MyBatis 二级缓存中是等价的,都是指同一个 Mapper 的所有操作共享缓存。

二级缓存配置

  1. mybatis-config.xml中启用二级缓存:
XML 复制代码
<settings>
  <setting name="cacheEnabled" value="true"/>
</settings>
  1. Mapper.xml中配置二级缓存:
XML 复制代码
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
  • eviction:缓存回收策略常用的有:
    • FifoCache:先进先出。
    • LruCache:最近最少使用。
    • SoftCache:基于软引用的缓存。
    • WeakCache:基于弱引用的缓存。
  • flushInterval:缓存刷新间隔(毫秒),每隔指定时间,缓存中的数据会被清空。
  • size:缓存对象的最大数量。
  • readOnly:是否只读(true表示只读,缓存对象不会被修改)。
  • implementation:缓存实现类。默认是 PerpetualCache,也可以指定其他实现(如集成 Redis)。
  1. 在 Mapper 接口上添加 @CacheNamespace 注解:
java 复制代码
import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.cache.decorators.FifoCache;
import org.apache.ibatis.cache.impl.PerpetualCache;
import org.apache.ibatis.annotations.CacheNamespace;

@CacheNamespace(
    eviction = FifoCache.class,       // 缓存回收策略:先进先出
    flushInterval = 60000,           // 缓存刷新间隔:60000 毫秒(60秒)
    size = 512,                      // 缓存对象的最大数量:512
    readWrite = false                // 是否可读写:false 表示只读
)
public interface UserMapper {
    User getUserById(int id);
    void updateUser(User user);
}
  1. 确保实体类实现 Serializable 接口:

由于二级缓存会将数据存储到外部存储(如内存缓存或分布式缓存),因此缓存的对象必须实现 Serializable 接口。

java 复制代码
import java.io.Serializable;

public class User implements Serializable {
    private int id;
    private String name;
    private String email;

    // Getter 和 Setter 方法
}
java 复制代码
try (SqlSession session1 = sqlSessionFactory.openSession()) {
    UserMapper mapper = session1.getMapper(UserMapper.class);
    User user1 = mapper.getUserById(1); // 查询数据库并存入二级缓存
}
try (SqlSession session2 = sqlSessionFactory.openSession()) {
    UserMapper mapper = session2.getMapper(UserMapper.class);
    User user2 = mapper.getUserById(1); // 从二级缓存获取
    System.out.println(user1 == user2); // true
}
  • 注解优先级高于 XML 配置: 如果同时在 Mapper 接口和 Mapper XML 文件中配置了二级缓存,注解的配置会优先生效。
  • 缓存对象序列化 : 二级缓存中存储的对象必须实现 Serializable 接口,否则可能会抛出序列化异常。
  • 事务管理: 在事务提交后,查询结果才会被存储到二级缓存中。如果事务回滚,缓存不会被更新。
  • 缓存失效 : 当执行 INSERTUPDATEDELETE 操作时,二级缓存会被清空,以保证数据一致性。
  • 适用场景
    • 查询操作多,增删改操作少。
    • 业务以单表操作为主,表间关联较少

一级缓存 和 二级缓存 区别

  1. 作用范围

|------|---------------------------------------------------------------|
| 缓存类型 | 作用范围 |
| 一级缓存 | 单个 SqlSession级别。同一个 SqlSession内的查询共享一级缓存。 |
| 二级缓存 | Mapper级别(namespace级别)。同一个 Mapper的不同 SqlSession共享二级缓存。 |

  1. 生命周期

|------|----------------------------------------------------------------|
| 缓存类型 | 生命周期 |
| 一级缓存 | 生命周期与 SqlSession一致。SqlSession关闭时,一级缓存清空。 |
| 二级缓存 | 生命周期独立于 SqlSession。二级缓存数据会一直存在,直到显式清空或达到缓存策略的限制(如时间间隔、大小限制)。 |

  1. 配置方式

|------|--------------------------------------------------------------------------------|
| 缓存类型 | 配置方式 |
| 一级缓存 | 默认开启,无法关闭。无需任何配置。 |
| 二级缓存 | 需要手动配置。可以在 Mapper接口上使用 @CacheNamespace注解,或在 Mapper.xml文件中配置 <cache>标签。 |

  1. 缓存失效条件

|------|--------------------------------------------------------------------------------------|
| 缓存类型 | 失效条件 |
| 一级缓存 | * 执行 insertupdatedelete操作; * 调用 SqlSession.clearCache(); * SqlSession关闭。 |
| 二级缓存 | * 执行 insertupdatedelete操作; * 达到缓存策略限制(如时间间隔、大小限制); * 显式调用清空缓存的方法。 |

  1. 数据共享性

|------|----------------------|
| 缓存类型 | 数据共享性 |
| 一级缓存 | 不支持跨 SqlSession共享。 |
| 二级缓存 | 支持跨 SqlSession共享。 |

  1. 使用场景

|------|------------------------------------|
| 缓存类型 | 适用场景 |
| 一级缓存 | 适用于单个 SqlSession内的重复查询。 |
| 二级缓存 | 适用于跨 SqlSession的重复查询,尤其是读多写少的场景。 |

  1. 缓存数据存储

|------|------------------------------|
| 缓存类型 | 存储方式 |
| 一级缓存 | 存储在内存中,与 SqlSession绑定。 |
| 二级缓存 | 默认存储在内存中,但可以集成外部缓存(如 Redis)。 |

简写:

|-------|-------------------------|-----------------------------|
| 特性 | 一级缓存 | 二级缓存 |
| 作用范围 | 单个 SqlSession | 跨 SqlSessionMapper级别) |
| 生命周期 | 与 SqlSession一致 | 独立于 SqlSession |
| 配置方式 | 默认开启,无需配置 | 需手动配置(注解或 XML) |
| 失效条件 | 执行增删改操作、SqlSession关闭等 | 执行增删改操作、达到缓存策略限制等 |
| 数据共享性 | 不支持跨 SqlSession共享 | 支持跨 SqlSession共享 |
| 适用场景 | 单个 SqlSession内的重复查询 | 跨 SqlSession的重复查询,读多写少的场景 |
| 存储方式 | 内存存储 | 内存存储或外部缓存(如 Redis) |

Spring的缓存机制

Spring 本身没有内置的缓存实现,而是通过缓存抽象层与各种缓存框架集成。

开发者可以根据需求选择基于 JVM 的缓存(如 Caffeine、EhCache)或第三方分布式缓存(如 Redis、Memcached)。

这种设计使得 Spring 的缓存功能非常灵活,能够适应不同的应用场景和性能需求。

Spring 的缓存抽象层是一个通用的缓存框架,它定义了缓存操作的接口(如 Cache****和 CacheManager**,springboot会详细讲到),但具体的缓存实现需要通过第三方库或基于 JVM 的缓存来提供。**

Spring 支持的缓存类型:

根据缓存的存储位置和实现方式,Spring 支持的缓存可以分为以下几类:

1. 基于 JVM 的缓存

这些缓存完全运行在 Java 虚拟机内存中,不需要外部服务。

常见的基于 JVM 的缓存包括:

  • Simple(简单缓存):Spring 提供的默认缓存实现,基于 ConcurrentHashMap,适合简单的应用场景。
  • Caffeine:高性能的本地缓存库,支持多种缓存策略(如 LRU、TTL 等)。
  • EhCache:功能丰富的本地缓存框架,支持内存和磁盘存储,以及缓存的持久化。
  • Guava Cache:Google 提供的本地缓存库,适合简单的缓存需求。

2. 第三方分布式缓存

这些缓存运行在独立的服务器上,支持分布式环境,适合高并发和大规模数据存储。常见的分布式缓存包括:

  • Redis:高性能的分布式缓存系统,支持多种数据结构(如字符串、列表、哈希等)。
  • Memcached:轻量级的分布式缓存系统,适合存储简单的键值对数据。
  • Hazelcast:支持内存数据网格的分布式缓存系统。

本文着重Redis、Caffeine 和 EhCache 在 Sprig 项目中的集成。

2.1 Redis 在 Spring 中的集成

Redis 是一个独立的缓存服务器,需要在服务器或本地单独安装和运行。

  1. 安装 Redis:
    • 在服务器上安装 Redis,并确保其运行在某个端口上(默认是 6379)。
    • 配置 Redis 的 redis.conf 文件(可选)。
  1. 在 Spring 项目中集成 Redis:
    • 添加依赖(使用 Maven):
XML 复制代码
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-redis</artifactId>
    <version>你的版本号</version>
</dependency>
<dependency>
    <groupId>io.lettuce</groupId>
    <artifactId>lettuce-core</artifactId>
    <version>你的版本号</version>
</dependency>

配置 Redis 连接工厂和 RedisTemplate

java 复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;

@Configuration
public class RedisConfig {
    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        return new LettuceConnectionFactory("localhost", 6379);
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new JdkSerializationRedisSerializer());
        return template;
    }
}

使用 RedisTemplate

java 复制代码
@Service
public class RedisService {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    public void set(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }

    public Object get(String key) {
        return redisTemplate.opsForValue().get(key);
    }
}

在 Spring 项目中使用 Redis 时,需要配置 RedisConnectionFactoryRedisTemplate,并通过这些组件与 Redis 服务器进行交互。

2.2 Caffeine 在 Spring 中的集成

Caffeine 是一个本地缓存库,完全运行在 JVM 内存中,不需要单独安装任何服务。

Caffeine 在 Spring 中的缓存是存储在 JVM 内存中的。Caffeine 是一个高性能的本地缓存库,它的设计目标是提供快速、低延迟的缓存服务,适用于单机应用。

Caffeine 缓存存储在 JVM 内存中的特点

  1. 高性能:由于缓存数据存储在本地 JVM 内存中,访问速度非常快,适合对性能要求较高的场景。

  2. 本地缓存:每个应用实例都有自己的缓存空间,缓存数据仅对当前应用实例可见。

  3. 内存限制:缓存的大小受限于 JVM 的可用内存,可以通过配置最大容量来避免内存溢出

  4. 添加依赖:

XML 复制代码
<dependency>
  <groupId>com.github.ben-manes.caffeine</groupId>
  <artifactId>caffeine</artifactId>
  <version>你的版本号</version>
</dependency>

2.配置 CaffeineCacheManager:

java 复制代码
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableCaching
public class CaffeineConfig {
    @Bean
    public CacheManager cacheManager() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager("myCache");
        cacheManager.setCaffeine(Caffeine.newBuilder()
                                 .expireAfterWrite(10, TimeUnit.MINUTES)
                                 .maximumSize(1000));
        return cacheManager;
    }
}

3.使用缓存:

java 复制代码
@Service
public class MyService {
    @Cacheable(value = "myCache", key = "#id")
    public String getData(String id) {
        return "My ID: " + id;
    }
}

Caffeine 是一个纯 Java 缓存库,运行在 JVM 内存中。

在 Spring 项目中使用 Caffeine 时,只需要添加依赖并配置 CaffeineCacheManager,无需任何外部服务。

2.3EhCache 在 Spring 中的集成

EhCache 是一个本地缓存框架,支持内存和磁盘存储,同样不需要单独安装任何服务。

在 Spring 中使用 EhCache 时,缓存数据的存储位置取决于 EhCache 的配置方式。EhCache 支持多种存储策略,包括内存(堆内)、堆外内存(off-heap)以及磁盘存储。

  1. 堆内内存(Heap):
    • 缓存数据存储在 JVM 的堆内存中。
    • 适用于缓存数据量较小且对性能要求较高的场景。
  1. 堆外内存(Off-Heap):
    • 缓存数据存储在 JVM 堆外内存中。
    • 堆外内存不属于 JVM 的堆空间,因此可以避免垃圾回收(GC)的干扰。
    • 数据需要序列化后存储,读取时需要反序列化。
  1. 磁盘存储(Disk):
    • 缓存数据可以溢出到磁盘,支持持久化。
    • 可以在应用重启后恢复缓存数据。
    • 配置磁盘存储路径时,可以指定一个目录来存储缓存文件。

1.添加依赖:

XML 复制代码
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context-support</artifactId>
  <version>你的版本号</version>
</dependency>
<dependency>
  <groupId>org.ehcache</groupId>
  <artifactId>ehcache</artifactId>
  <version>你的版本号</version>
</dependency>

2.配置 EhCache:

  • 创建 ehcache.xml 配置文件:
XML 复制代码
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
  <cache name="myCache"
    maxEntriesLocalHeap="1000"
    eternal="false"
    timeToIdleSeconds="300"
    timeToLiveSeconds="600">
  </cache>
</ehcache>

配置 EhCacheCacheManager

java 复制代码
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.ehcache.EhCacheCacheManager;
import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;

@Configuration
@EnableCaching
public class EhCacheConfig {
    @Bean
    public EhCacheManagerFactoryBean ehCacheManagerFactoryBean() {
        EhCacheManagerFactoryBean cacheManagerFactoryBean = new EhCacheManagerFactoryBean();
        cacheManagerFactoryBean.setConfigLocation(new ClassPathResource("ehcache.xml"));
        cacheManagerFactoryBean.setShared(true);
        return cacheManagerFactoryBean;
    }

    @Bean
    public CacheManager cacheManager(EhCacheManagerFactoryBean ehCacheManagerFactoryBean) {
        return new EhCacheCacheManager(ehCacheManagerFactoryBean.getObject());
    }
}

3.使用缓存:

java 复制代码
@Service
public class MyService {
    @Cacheable(value = "myCache", key = "#id")
    public String getData(String id) {
        return "Data for ID: " + id;
    }
}

EhCache 是一个本地缓存框架,支持内存和磁盘存储。

在 Spring 项目中使用 EhCache 时,需要添加依赖并配置 EhCacheCacheManager,无需任何外部服务。

SpringBoot的缓存机制

和 Spring 一样,都是通过Spring Cache 抽象层 来实现缓存功能。

Spring Cache 抽象层是一个通用的缓存框架,它提供了一组标准的缓存接口和注解,使得开发者可以轻松地将缓存功能集成到应用程序中,而无需关心具体的缓存实现。

Spring Cache 抽象层的核心组件包括:

  1. @EnableCaching:用于启用缓存功能的注解,通常添加在配置类或主类上。
  2. @Cacheable:用于标记方法的返回值可以被缓存。
  3. @CachePut:用于更新缓存中的数据。
  4. @CacheEvict:用于清除缓存中的数据。
  5. CacheManager:负责管理多个缓存实例。
  6. Cache:定义了缓存的基本操作接口。

虽然 Spring 和 Spring Boot 都使用了 Spring Cache 抽象层,但它们在 配置方式和默认行为 上有所不同:

1 . Spring(非 Spring Boot)

在传统的 Spring 应用程序中:

  • 没有内置的缓存实现:Spring Cache 抽象层本身不提供具体的缓存实现。
  • 需要手动配置:开发者需要手动配置 CacheManager 和具体的缓存实现(如 EhCache、Caffeine 等)。
java 复制代码
@Configuration
@EnableCaching
public class CacheConfig {
    @Bean
    public CacheManager cacheManager() {
        // 配置 EhCache 或 Caffeine 等具体缓存实现
        return new EhCacheCacheManager(ehCacheManagerFactory().getObject());
    }
}

2. Spring Boot

Spring Boot 在 Spring Cache 抽象层的基础上进行了简化和自动配置:

  • 内置的默认缓存实现:如果没有显式配置任何缓存实现,Spring Boot 会自动启用一个基于 ConcurrentHashMap 的简单缓存实现(SimpleCacheManagerConcurrentMapCacheManager)。这可以看作是 Spring Boot 提供的"内置"缓存实现,但它仍然是基于 Spring Cache 抽象层的。
  • 自动配置:Spring Boot 会根据添加的依赖自动选择合适的缓存实现。例如:
    • 如果添加了 Caffeine 的依赖,Spring Boot 会自动配置 CaffeineCacheManager
    • 如果添加了 Redis 的依赖,Spring Boot 会自动配置 RedisCacheManager
XML 复制代码
<!-- 添加 Caffeine 依赖 -->
<dependency>
  <groupId>com.github.ben-manes.caffeine</groupId>
  <artifactId>caffeine</artifactId>
</dependency>
java 复制代码
@SpringBootApplication
@EnableCaching
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

总结:Spring 和 Spring Boot 的缓存机制

  1. Spring Cache 抽象层 是 Spring 框架的一部分,提供了一套通用的缓存接口和注解,但本身不提供具体的缓存实现。
  2. Spring(非 Spring Boot):
    • 没有内置的缓存实现。
    • 需要手动配置 CacheManager 和具体的缓存实现。
  1. Spring Boot:
    • 提供了一个基于 ConcurrentHashMap 的简单默认缓存实现(ConcurrentMapCacheManager)。
    • 根据添加的依赖自动配置更强大的缓存实现(如 Caffeine、Redis 等)。
    • 这种自动配置机制使得 Spring Boot 的缓存功能看起来像是"内置"的,但实际上它仍然是基于 Spring Cache 抽象层的。

Spring 和 Spring Boot 的缓存抽象层对比

|---------|-------------------------------------------|--------------------------|
| 特性 | Spring | Spring Boot |
| 配置方式 | 手动配置 CacheManager和缓存实现 | 自动配置,通过添加依赖启用缓存功能 |
| 默认缓存实现 | 无默认实现,需手动指定 | 默认使用 ConcurrentHashMap |
| 集成第三方缓存 | 手动添加依赖并配置 | 添加依赖后自动配置,支持多种缓存实现 |
| 注解支持 | 支持 @Cacheable@CachePut@CacheEvict | 支持相同的注解,但配置更简化 |
| 灵活性 | 高度灵活,适合复杂配置 | 简化配置,适合快速开发 |

也就是 Spring 和 Spring Boot,都通过 Spring Cache 抽象层 提供缓存功能。

而Spring Boot 在此基础上进行了简化和自动配置,使得开发者可以更轻松地启用和集成缓存,而无需手动配置复杂的 CacheManager 和缓存实现。

SpringBoot的缓存机制属于是:

  • 开箱即用:即使没有显式配置任何缓存实现,Spring Boot 也会提供一个基于内存的简单缓存(默认使用 ConcurrentHashMap)。这种缓存适合简单的开发场景或轻量级的生产环境。
  • 灵活性:在需要更强大的缓存功能时,Spring Boot 可以轻松集成第三方缓存解决方案(如 Caffeine、EhCache、Redis 等)。
  • 自动配置:Spring Boot 通过 spring-boot-starter-cache 模块自动配置缓存功能。它会根据项目中添加的依赖自动选择合适的缓存实现。
    • 如果没有添加任何缓存依赖,Spring Boot 会启用默认的内存缓存。
    • 如果添加了第三方缓存依赖(如 Caffeine 或 Redis),Spring Boot 会自动配置相应的缓存实现。
  • 简化配置:开发者只需添加依赖并启用缓存功能(通过 @EnableCaching),而无需手动配置复杂的 CacheManager

Redis 在 SpringBoot 中的集成

1. 添加依赖

在项目的 pom.xml 文件中添加 Spring Boot 提供的 Redis 依赖

XML 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

如果需要更高级的 Redis 功能(如分布式锁),可以添加 Redisson 的依赖:

XML 复制代码
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.17.0</version> <!-- 请根据需要选择合适的版本 -->
</dependency>

2. 配置 Redis

application.ymlapplication.properties 文件中配置 Redis 的连接信息。

application.yml 示例:

XML 复制代码
spring:
  redis:
    host: 127.0.0.1  # Redis 服务器地址
    port: 6379       # Redis 服务器端口
    password:        # Redis 密码(如果有)
    database: 0      # 数据库索引(默认为0)
    timeout: 1800000 # 连接超时时间(毫秒)
    lettuce:
      pool:
        max-active: 20  # 连接池最大连接数
        max-wait: -1    # 最大阻塞等待时间(负数表示无限制)
        max-idle: 5     # 最大空闲连接数
        min-idle: 0     # 最小空闲连接数

3. 启用缓存

在主应用类上添加 @EnableCaching 注解:

java 复制代码
@SpringBootApplication
@EnableCaching
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

4. 配置 RedisTemplate(可选)

如果需要操作复杂的数据类型(如对象),可以自定义 RedisTemplate

java 复制代码
@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        template.setKeySerializer(new StringRedisSerializer()); // 设置键的序列化器
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); // 设置值的序列化器
        return template;
    }
}

5. 编写 Redis 操作类

创建一个服务类来封装 Redis 的常用操作:

java 复制代码
@Service
public class RedisService {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    public void setValue(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }

    public Object getValue(String key) {
        return redisTemplate.opsForValue().get(key);
    }

    public void deleteValue(String key) {
        redisTemplate.delete(key);
    }
}

6. 使用 Redis

在业务逻辑中注入 RedisService 或直接使用 RedisTemplate 来操作 Redis。

需要注意的有:

序列化问题:默认情况下,Spring Boot 使用 JdkSerializationRedisSerializer,存储的数据为二进制格式。建议使用 GenericJackson2JsonRedisSerializer 将对象序列化为 JSON 格式,便于调试和监控。

缓存穿透问题:可以通过在缓存中设置空值或使用布隆过滤器来解决。

缓存配置:可以在 spring.cache.redis 下配置缓存相关参数,例如 time-to-livekey-prefix 等。

MYSQL 的缓存机制

MySQL 本身提供了多种缓存机制,用于优化查询性能和减少对磁盘的访问。

1. 查询缓存(Query Cache)

查询缓存是 MySQL 提供的一种缓存机制,用于缓存查询结果。当相同的查询再次执行时,MySQL 可以直接从查询缓存中返回结果,而无需重新执行查询。

特点
  • 缓存查询结果:将查询结果存储在内存中,减少对磁盘的访问。
  • 自动失效:当相关表的数据发生变化时,查询缓存中的相关结果会自动失效。
  • 性能优化:对于重复执行的查询,可以显著提高性能。
配置

查询缓存的大小可以通过以下参数配置:

  • query_cache_size:查询缓存的大小(单位:字节)。
  • query_cache_type:查询缓存的类型(ONOFFDEMAND)。
  • query_cache_limit:单个查询结果的最大缓存大小(单位:字节)。
sql 复制代码
SET GLOBAL query_cache_size = 1024 * 1024 * 100; -- 100MB
SET GLOBAL query_cache_type = 'ON';
SET GLOBAL query_cache_limit = 1024 * 1024 * 2; -- 2MB

2. InnoDB 缓冲池(Buffer Pool)

InnoDB 缓冲池是 InnoDB 存储引擎的核心缓存机制,用于缓存表数据和索引数据。它显著提高了对表和索引的读写性能。

特点
  • 缓存表数据和索引:将表数据和索引数据存储在内存中,减少对磁盘的访问。
  • LRU 算法:使用最近最少使用(LRU)算法管理缓存数据。
  • 性能优化:对于频繁访问的数据,可以显著提高读写性能。
配置

InnoDB 缓冲池的大小可以通过以下参数配置:

  • innodb_buffer_pool_size:InnoDB 缓冲池的大小(单位:字节)。
  • innodb_buffer_pool_instances:InnoDB 缓冲池的实例数量。
sql 复制代码
SET GLOBAL innodb_buffer_pool_size = 1024 * 1024 * 1024 * 8; -- 8GB
SET GLOBAL innodb_buffer_pool_instances = 8;

3. MyISAM 缓存

MyISAM 存储引擎也提供了缓存机制,主要用于缓存索引数据。

特点
  • 缓存索引数据:将索引数据存储在内存中,减少对磁盘的访问。
  • 性能优化:对于频繁访问的索引,可以显著提高查询性能。
配置

MyISAM 缓存的大小可以通过以下参数配置:

  • key_buffer_size:MyISAM 缓存的大小(单位:字节)。
sql 复制代码
SET GLOBAL key_buffer_size = 1024 * 1024 * 100; -- 100MB

4. 二进制日志缓存(Binary Log Cache)

二进制日志缓存用于缓存二进制日志(Binary Log)的写操作,提高写入性能。

特点
  • 缓存二进制日志:将二进制日志的写操作缓存到内存中,减少对磁盘的访问。
  • 性能优化:对于频繁的写操作,可以显著提高性能。
配置

二进制日志缓存的大小可以通过以下参数配置:

  • binlog_cache_size:二进制日志缓存的大小(单位:字节)。
sql 复制代码
SET GLOBAL binlog_cache_size = 1024 * 1024 * 4; -- 4MB

5. 临时表缓存(Temporary Table Cache)

临时表缓存用于缓存临时表的创建和销毁操作,提高临时表的使用效率。

特点
  • 缓存临时表:将临时表的创建和销毁操作缓存到内存中,减少对磁盘的访问。
  • 性能优化:对于频繁使用的临时表,可以显著提高性能。
配置

临时表缓存的大小可以通过以下参数配置:

  • tmp_table_size:临时表缓存的大小(单位:字节)。
  • max_heap_table_size:内存表(Heap Table)的最大大小(单位:字节)。
sql 复制代码
SET GLOBAL tmp_table_size = 1024 * 1024 * 100; -- 100MB
SET GLOBAL max_heap_table_size = 1024 * 1024 * 100; -- 100MB

6. 表缓存(Table Cache)

表缓存用于缓存表的打开操作,减少对表的重复打开。

特点
  • 缓存表的打开操作:将表的打开操作缓存到内存中,减少对磁盘的访问。
  • 性能优化:对于频繁访问的表,可以显著提高性能。
配置

表缓存的大小可以通过以下参数配置:

  • table_open_cache:表缓存的大小(单位:表的数量)。
sql 复制代码
SET GLOBAL table_open_cache = 2000;

不过需要注意的是:

  • 查询缓存的限制:查询缓存对 SQL 语句的大小写和空格敏感,且只对 SELECT 查询有效。
  • InnoDB 缓冲池的大小:建议将 innodb_buffer_pool_size 设置为可用内存的 70% 左右,以充分利用内存。
  • MyISAM 缓存的限制:MyISAM 缓存仅对索引数据有效,不缓存表数据。
  • 性能监控:定期监控缓存的使用情况,调整缓存大小以优化性能。

mysql的缓存机制

MySQL 本身提供了多种缓存机制,用于优化查询性能和减少对磁盘的访问。

1. 查询缓存(Query Cache)

查询缓存是 MySQL 提供的一种缓存机制,用于缓存查询结果。当相同的查询再次执行时,MySQL 可以直接从查询缓存中返回结果,而无需重新执行查询。

从 MySQL 5.7 开始,查询缓存的默认开启状态被改为关闭。主要是因为查询缓存存在一些限制和缺陷,可能导致性能问题。

例如,查询缓存对于更新操作(INSERT、UPDATE 和 DELETE)会导致缓存失效,这意味着每次更新后都需要重新执行查询。此外,查询缓存的锁机制在高并发环境下也可能成为性能瓶颈。

特点

  • 缓存查询结果:将查询结果存储在内存中,减少对磁盘的访问。
  • 自动失效:当相关表的数据发生变化时,查询缓存中的相关结果会自动失效。
  • 性能优化:对于重复执行的查询,可以显著提高性能。

查询缓存的大小可以通过以下参数配置:

  • query_cache_size:查询缓存的大小(单位:字节)。
  • query_cache_type:查询缓存的类型(ONOFFDEMAND)。
  • query_cache_limit:单个查询结果的最大缓存大小(单位:字节)。
sql 复制代码
SET GLOBAL query_cache_size = 1024 * 1024 * 100; -- 100MB
SET GLOBAL query_cache_type = 'ON';
SET GLOBAL query_cache_limit = 1024 * 1024 * 2; -- 2MB

查看查询缓存状态:

sql 复制代码
SHOW VARIABLES LIKE 'query_cache_type';
SHOW VARIABLES LIKE 'query_cache_size';

2. InnoDB 缓冲池(Buffer Pool)

在 MySQL 5.5.5 之前的版本中,InnoDB 缓冲池并不是默认开启的。从 MySQL 5.5.5 开始,InnoDB 成为默认的存储引擎,InnoDB 缓冲池也随之默认开启。

InnoDB 缓冲池是 InnoDB 存储引擎的核心缓存机制,用于缓存表数据和索引数据。它显著提高了对表和索引的读写性能。

特点

  • 缓存表数据和索引:将表数据和索引数据存储在内存中,减少对磁盘的访问。
  • LRU 算法:使用最近最少使用(LRU)算法管理缓存数据。
  • 性能优化:对于频繁访问的数据,可以显著提高读写性能。

配置

InnoDB 缓冲池的大小可以通过以下参数配置:

  • innodb_buffer_pool_size:InnoDB 缓冲池的大小(单位:字节)。
  • innodb_buffer_pool_instances:InnoDB 缓冲池的实例数量。
sql 复制代码
SET GLOBAL innodb_buffer_pool_size = 1024 * 1024 * 1024 * 8; -- 8GB
SET GLOBAL innodb_buffer_pool_instances = 8;

3. MyISAM 缓存

MyISAM 存储引擎也提供了缓存机制,主要用于缓存索引数据。

但其是否默认开启和配置可能会因 MySQL 版本和具体安装配置而有所不同。

特点

  • 缓存索引数据:将索引数据存储在内存中,减少对磁盘的访问。
  • 性能优化:对于频繁访问的索引,可以显著提高查询性能。

配置

MyISAM 缓存的大小可以通过以下参数配置:

  • key_buffer_size:MyISAM 缓存的大小(单位:字节)。
sql 复制代码
SET GLOBAL key_buffer_size = 1024 * 1024 * 100; -- 100MB

4. 二进制日志缓存(Binary Log Cache)

二进制日志缓存用于缓存二进制日志(Binary Log)的写操作,提高写入性能。

特点

  • 缓存二进制日志:将二进制日志的写操作缓存到内存中,减少对磁盘的访问。
  • 性能优化:对于频繁的写操作,可以显著提高性能。

配置

二进制日志缓存的大小可以通过以下参数配置:

  • binlog_cache_size:二进制日志缓存的大小(单位:字节)。
sql 复制代码
SET GLOBAL binlog_cache_size = 1024 * 1024 * 4; -- 4MB

MySQL 的二进制日志缓存(Binary Log Cache)是否默认开启,取决于 MySQL 的版本和具体配置。

  1. 二进制日志(Binlog)是否默认开启:
    • 在 MySQL 5.7 版本中,二进制日志默认是不开启的。
    • 在 MySQL 8.0 版本中,二进制日志默认是开启的。
  1. 二进制日志缓存(Binary Log Cache):
    • 当二进制日志(Binlog)被开启时,二进制日志缓存(Binary Log Cache)会自动启用。这是因为二进制日志缓存是事务性操作的必要机制。
    • 二进制日志缓存的大小可以通过 binlog_cache_size 参数进行配置。
  1. 如何确认二进制日志是否开启:
    • 可以通过以下命令查看二进制日志是否开启:sql复制
sql 复制代码
SHOW VARIABLES LIKE 'log_bin';

如果返回值为 ON,则表示二进制日志已开启。

  1. 如何开启二进制日志:
    • 编辑 MySQL 的配置文件(如 my.cnfmy.ini),添加以下配置:ini复制
sql 复制代码
[mysqld]
log-bin=mysql-bin
    • 重启 MySQL 服务以使配置生效。

5. 临时表缓存(Temporary Table Cache)

临时表缓存用于缓存临时表的创建和销毁操作,提高临时表的使用效率。

特点

  • 缓存临时表:将临时表的创建和销毁操作缓存到内存中,减少对磁盘的访问。
  • 性能优化:对于频繁使用的临时表,可以显著提高性能。

配置

临时表缓存的大小可以通过以下参数配置:

  • tmp_table_size:临时表缓存的大小(单位:字节)。
  • max_heap_table_size:内存表(Heap Table)的最大大小(单位:字节)。
sql 复制代码
SET GLOBAL tmp_table_size = 1024 * 1024 * 100; -- 100MB
SET GLOBAL max_heap_table_size = 1024 * 1024 * 100; -- 100MB

6. 表缓存(Table Cache)

表缓存用于缓存表的打开操作,减少对表的重复打开。

特点

  • 缓存表的打开操作:将表的打开操作缓存到内存中,减少对磁盘的访问。
  • 性能优化:对于频繁访问的表,可以显著提高性能。

配置

表缓存的大小可以通过以下参数配置:

  • table_open_cache:表缓存的大小(单位:表的数量)。
sql 复制代码
SET GLOBAL table_open_cache = 2000;

需要注意的是:

  • 查询缓存的限制:查询缓存对 SQL 语句的大小写和空格敏感,且只对 SELECT 查询有效。
  • InnoDB 缓冲池的大小:建议将 innodb_buffer_pool_size 设置为可用内存的 70% 左右,以充分利用内存。
  • MyISAM 缓存的限制:MyISAM 缓存仅对索引数据有效,不缓存表数据。
  • 性能监控:定期监控缓存的使用情况,调整缓存大小以优化性能。

Java 本身的缓存

再回到这个问题,Java自己的缓存。

首先,Java 本身并没有一个内置的、通用的缓存机制,但它提供了许多工具和特性,可以用来实现缓存功能。这些工具包括语言特性、标准库中的类,以及一些设计模式。

1. 常量池(Constant Pool)

Java 的常量池是一种特殊的缓存机制,用于存储字符串常量、类和接口的名称、字段名等。常量池是 JVM 的一部分,它确保了字符串常量的唯一性。

java 复制代码
String str1 = "Hello";
String str2 = "Hello";
System.out.println(str1 == str2); // true,因为它们引用同一个常量池中的对象

2. String.intern()

String.intern() 方法会将字符串存储在常量池中,如果常量池中已经存在相同的字符串,则返回常量池中的引用。这可以看作是一种简单的缓存机制。

java 复制代码
String str1 = new String("Hello");
String str2 = new String("Hello").intern();
System.out.println(str1 == str2); // false,因为 str1 是通过 new 创建的
System.out.println(str1.intern() == str2); // true,因为 intern() 会返回常量池中的引用

3. HashMap 或其他集合

虽然 HashMap 本身不是缓存,但可以通过它实现简单的缓存逻辑。HashMap 提供了快速的键值对存储和检索功能,适合用作本地缓存。

java 复制代码
import java.util.HashMap;

public class SimpleCache {
    private final HashMap<String, String> cache = new HashMap<>();

    public String get(String key) {
        return cache.get(key);
    }

    public void put(String key, String value) {
        cache.put(key, value);
    }
}

4. WeakHashMap

WeakHashMap 是一种特殊的 HashMap,它的键是弱引用。当键被垃圾回收器回收时,对应的值也会被移除。这可以用于实现简单的"弱缓存",避免内存泄漏。

java 复制代码
import java.util.WeakHashMap;

public class WeakCache {
    private final WeakHashMap<String, String> cache = new WeakHashMap<>();

    public String get(String key) {
        return cache.get(key);
    }

    public void put(String key, String value) {
        cache.put(key, value);
    }
}

5. SoftReferenceWeakReference

SoftReferenceWeakReference 是 Java 提供的两种引用类型,可以用于实现缓存。SoftReference 在内存不足时才会被回收,而 WeakReference 在下一次垃圾回收时就会被回收。

java 复制代码
import java.lang.ref.SoftReference;
import java.util.HashMap;

public class SoftCache {
    private final HashMap<String, SoftReference<String>> cache = new HashMap<>();

    public String get(String key) {
        SoftReference<String> ref = cache.get(key);
        return ref != null ? ref.get() : null;
    }

    public void put(String key, String value) {
        cache.put(key, new SoftReference<>(value));
    }
}

6. java.util.concurrent

Java 的 java.util.concurrent 包提供了许多线程安全的集合类,如 ConcurrentHashMap,可以用于实现高性能的本地缓存。

java 复制代码
import java.util.concurrent.ConcurrentHashMap;

public class ConcurrentCache {
    private final ConcurrentHashMap<String, String> cache = new ConcurrentHashMap<>();

    public String get(String key) {
        return cache.get(key);
    }

    public void put(String key, String value) {
        cache.put(key, value);
    }
}

7. 设计模式:单例模式

单例模式可以看作是一种简单的缓存机制,用于确保某个类的实例在应用程序中只创建一次。单例模式常用于管理共享资源,如配置文件、数据库连接池等。

java 复制代码
public class Singleton {
    private static Singleton instance;

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

8. Java 缓存库

虽然 Java 本身没有内置的缓存库,但有许多第三方库可以实现更复杂的缓存功能,如:

  • Caffeine:高性能的本地缓存库。
  • EhCache:功能丰富的本地缓存库。
  • Guava Cache:Google 提供的本地缓存库。

总结下来:

Java 本身没有内置的通用缓存机制,但它提供了许多工具和特性,可以用来实现缓存功能。

  • 常量池:用于存储字符串常量。
  • HashMapConcurrentHashMap:用于实现简单的本地缓存。
  • SoftReferenceWeakReference:用于实现弱缓存。
  • 设计模式(如单例模式):用于管理共享资源。

近日总结:完蛋了,昨天上午面的二面到现在还没有通知结果.................................................

相关推荐
ChinaRainbowSea3 分钟前
Linux: Centos7 Cannot find a valid baseurl for repo: base/7/x86_64 解决方案
java·linux·运维·服务器·docker·架构
囧囧 O_o4 分钟前
Java 实现 Oracle 的 MONTHS_BETWEEN 函数
java·oracle
去看日出7 分钟前
RabbitMQ消息队列中间件安装部署教程(Windows)-2025最新版详细图文教程(附所需安装包)
java·windows·中间件·消息队列·rabbitmq
计算机-秋大田9 分钟前
基于Spring Boot的宠物健康顾问系统的设计与实现(LW+源码+讲解)
java·vue.js·spring boot·后端·课程设计
食指Shaye11 分钟前
Chrome 中清理缓存的方法
前端·chrome·缓存
JouJz16 分钟前
Java虚拟机之垃圾收集(一)
java·开发语言·jvm
曹天骄27 分钟前
mybatis-plus+springboot3项目实现分页
mybatis
源码姑娘29 分钟前
基于DeepSeek的智慧医药系统(源码+部署教程)
java·人工智能·程序人生·毕业设计·springboot·健康医疗·课程设计
morris13134 分钟前
【redis】布隆过滤器的Java实现
java·redis·布隆过滤器
椰椰椰耶38 分钟前
【redis】全局命令set、get、keys
数据库·redis·缓存