目录
[Mapper 级别和 namespace 级别的关系](#Mapper 级别和 namespace 级别的关系)
[2.1 Redis 在 Spring 中的集成](#2.1 Redis 在 Spring 中的集成)
[2.2 Caffeine 在 Spring 中的集成](#2.2 Caffeine 在 Spring 中的集成)
[2.3EhCache 在 Spring 中的集成](#2.3EhCache 在 Spring 中的集成)
[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))
[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
关闭时,一级缓存清空。 - 失效条件:
-
- 执行
insert
、update
、delete
操作时,一级缓存会被清空。 - 调用
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.xml
和Mapper.xml
中配置。 - 缓存实现:默认使用内存缓存,也可集成Redis等外部缓存。
- 失效条件 :在
Mapper
中执行insert
、update
、delete
操作时,二级缓存会被清空。 - 存储位置:
-
- 默认存储在 JVM 内存中。
- 可以通过配置集成外部缓存,如 EhCache 或 Redis。
也有的说二级缓存的作用范围是 namespace 级别的,但其实namespace 级别和Mapper
级别这两种说法本质上是相同的,只是表述方式不同。
Mapper 级别和 namespace 级别的关系
Mapper
级别:指的是同一个Mapper
接口或 Mapper XML 文件的作用范围。在 MyBatis 中,每个Mapper
接口或 XML 文件都有一个唯一的namespace
。namespace
级别:namespace
是Mapper
的唯一标识,通常对应一个Mapper
接口或 XML 文件。二级缓存的作用范围是基于namespace
的,这意味着同一个namespace
下的所有操作共享同一个缓存。
所以,Mapper
级别和namespace
级别在 MyBatis 二级缓存中是等价的,都是指同一个 Mapper
的所有操作共享缓存。
二级缓存配置
- 在
mybatis-config.xml
中启用二级缓存:
XML
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
- 在
Mapper.xml
中配置二级缓存:
XML
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
eviction
:缓存回收策略常用的有:
-
FifoCache
:先进先出。LruCache
:最近最少使用。SoftCache
:基于软引用的缓存。WeakCache
:基于弱引用的缓存。
flushInterval
:缓存刷新间隔(毫秒),每隔指定时间,缓存中的数据会被清空。size
:缓存对象的最大数量。readOnly
:是否只读(true
表示只读,缓存对象不会被修改)。implementation
:缓存实现类。默认是PerpetualCache
,也可以指定其他实现(如集成 Redis)。
- 在 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);
}
- 确保实体类实现
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
接口,否则可能会抛出序列化异常。 - 事务管理: 在事务提交后,查询结果才会被存储到二级缓存中。如果事务回滚,缓存不会被更新。
- 缓存失效 : 当执行
INSERT
、UPDATE
或DELETE
操作时,二级缓存会被清空,以保证数据一致性。 - 适用场景:
-
- 查询操作多,增删改操作少。
- 业务以单表操作为主,表间关联较少
一级缓存 和 二级缓存 区别
- 作用范围
|------|---------------------------------------------------------------|
| 缓存类型 | 作用范围 |
| 一级缓存 | 单个 SqlSession
级别。同一个 SqlSession
内的查询共享一级缓存。 |
| 二级缓存 | Mapper
级别(namespace
级别)。同一个 Mapper
的不同 SqlSession
共享二级缓存。 |
- 生命周期
|------|----------------------------------------------------------------|
| 缓存类型 | 生命周期 |
| 一级缓存 | 生命周期与 SqlSession
一致。SqlSession
关闭时,一级缓存清空。 |
| 二级缓存 | 生命周期独立于 SqlSession
。二级缓存数据会一直存在,直到显式清空或达到缓存策略的限制(如时间间隔、大小限制)。 |
- 配置方式
|------|--------------------------------------------------------------------------------|
| 缓存类型 | 配置方式 |
| 一级缓存 | 默认开启,无法关闭。无需任何配置。 |
| 二级缓存 | 需要手动配置。可以在 Mapper
接口上使用 @CacheNamespace
注解,或在 Mapper.xml
文件中配置 <cache>
标签。 |
- 缓存失效条件
|------|--------------------------------------------------------------------------------------|
| 缓存类型 | 失效条件 |
| 一级缓存 | * 执行 insert
、update
、delete
操作; * 调用 SqlSession.clearCache()
; * SqlSession
关闭。 |
| 二级缓存 | * 执行 insert
、update
、delete
操作; * 达到缓存策略限制(如时间间隔、大小限制); * 显式调用清空缓存的方法。 |
- 数据共享性
|------|----------------------|
| 缓存类型 | 数据共享性 |
| 一级缓存 | 不支持跨 SqlSession
共享。 |
| 二级缓存 | 支持跨 SqlSession
共享。 |
- 使用场景
|------|------------------------------------|
| 缓存类型 | 适用场景 |
| 一级缓存 | 适用于单个 SqlSession
内的重复查询。 |
| 二级缓存 | 适用于跨 SqlSession
的重复查询,尤其是读多写少的场景。 |
- 缓存数据存储
|------|------------------------------|
| 缓存类型 | 存储方式 |
| 一级缓存 | 存储在内存中,与 SqlSession
绑定。 |
| 二级缓存 | 默认存储在内存中,但可以集成外部缓存(如 Redis)。 |
简写:
|-------|-------------------------|-----------------------------|
| 特性 | 一级缓存 | 二级缓存 |
| 作用范围 | 单个 SqlSession
| 跨 SqlSession
(Mapper
级别) |
| 生命周期 | 与 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 是一个独立的缓存服务器,需要在服务器或本地单独安装和运行。
- 安装 Redis:
-
- 在服务器上安装 Redis,并确保其运行在某个端口上(默认是 6379)。
- 配置 Redis 的
redis.conf
文件(可选)。
- 在 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 时,需要配置 RedisConnectionFactory
和 RedisTemplate
,并通过这些组件与 Redis 服务器进行交互。
2.2 Caffeine 在 Spring 中的集成
Caffeine 是一个本地缓存库,完全运行在 JVM 内存中,不需要单独安装任何服务。
Caffeine 在 Spring 中的缓存是存储在 JVM 内存中的。Caffeine 是一个高性能的本地缓存库,它的设计目标是提供快速、低延迟的缓存服务,适用于单机应用。
Caffeine 缓存存储在 JVM 内存中的特点
-
高性能:由于缓存数据存储在本地 JVM 内存中,访问速度非常快,适合对性能要求较高的场景。
-
本地缓存:每个应用实例都有自己的缓存空间,缓存数据仅对当前应用实例可见。
-
内存限制:缓存的大小受限于 JVM 的可用内存,可以通过配置最大容量来避免内存溢出
-
添加依赖:
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)以及磁盘存储。
- 堆内内存(Heap):
-
- 缓存数据存储在 JVM 的堆内存中。
- 适用于缓存数据量较小且对性能要求较高的场景。
- 堆外内存(Off-Heap):
-
- 缓存数据存储在 JVM 堆外内存中。
- 堆外内存不属于 JVM 的堆空间,因此可以避免垃圾回收(GC)的干扰。
- 数据需要序列化后存储,读取时需要反序列化。
- 磁盘存储(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 抽象层的核心组件包括:
@EnableCaching
:用于启用缓存功能的注解,通常添加在配置类或主类上。@Cacheable
:用于标记方法的返回值可以被缓存。@CachePut
:用于更新缓存中的数据。@CacheEvict
:用于清除缓存中的数据。CacheManager
:负责管理多个缓存实例。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
的简单缓存实现(SimpleCacheManager
或ConcurrentMapCacheManager
)。这可以看作是 Spring Boot 提供的"内置"缓存实现,但它仍然是基于 Spring Cache 抽象层的。 - 自动配置:Spring Boot 会根据添加的依赖自动选择合适的缓存实现。例如:
-
- 如果添加了 Caffeine 的依赖,Spring Boot 会自动配置
CaffeineCacheManager
。 - 如果添加了 Redis 的依赖,Spring Boot 会自动配置
RedisCacheManager
。
- 如果添加了 Caffeine 的依赖,Spring Boot 会自动配置
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 的缓存机制
- Spring Cache 抽象层 是 Spring 框架的一部分,提供了一套通用的缓存接口和注解,但本身不提供具体的缓存实现。
- Spring(非 Spring Boot):
-
- 没有内置的缓存实现。
- 需要手动配置
CacheManager
和具体的缓存实现。
- 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.yml
或 application.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-live
、key-prefix
等。
MYSQL 的缓存机制
MySQL 本身提供了多种缓存机制,用于优化查询性能和减少对磁盘的访问。
1. 查询缓存(Query Cache)
查询缓存是 MySQL 提供的一种缓存机制,用于缓存查询结果。当相同的查询再次执行时,MySQL 可以直接从查询缓存中返回结果,而无需重新执行查询。
特点
- 缓存查询结果:将查询结果存储在内存中,减少对磁盘的访问。
- 自动失效:当相关表的数据发生变化时,查询缓存中的相关结果会自动失效。
- 性能优化:对于重复执行的查询,可以显著提高性能。
配置
查询缓存的大小可以通过以下参数配置:
query_cache_size
:查询缓存的大小(单位:字节)。query_cache_type
:查询缓存的类型(ON
、OFF
或DEMAND
)。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
:查询缓存的类型(ON
、OFF
或DEMAND
)。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 的版本和具体配置。
- 二进制日志(Binlog)是否默认开启:
-
- 在 MySQL 5.7 版本中,二进制日志默认是不开启的。
- 在 MySQL 8.0 版本中,二进制日志默认是开启的。
- 二进制日志缓存(Binary Log Cache):
-
- 当二进制日志(Binlog)被开启时,二进制日志缓存(Binary Log Cache)会自动启用。这是因为二进制日志缓存是事务性操作的必要机制。
- 二进制日志缓存的大小可以通过
binlog_cache_size
参数进行配置。
- 如何确认二进制日志是否开启:
-
- 可以通过以下命令查看二进制日志是否开启:sql复制
sql
SHOW VARIABLES LIKE 'log_bin';
如果返回值为 ON
,则表示二进制日志已开启。
- 如何开启二进制日志:
-
- 编辑 MySQL 的配置文件(如
my.cnf
或my.ini
),添加以下配置:ini复制
- 编辑 MySQL 的配置文件(如
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. SoftReference
和 WeakReference
SoftReference
和 WeakReference
是 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 本身没有内置的通用缓存机制,但它提供了许多工具和特性,可以用来实现缓存功能。
- 常量池:用于存储字符串常量。
HashMap
和ConcurrentHashMap
:用于实现简单的本地缓存。SoftReference
和WeakReference
:用于实现弱缓存。- 设计模式(如单例模式):用于管理共享资源。

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