我来为你实现一个基于MySQL的Redis缓存工具类。这个工具类将提供完整的缓存功能,包括增删改查和过期时间管理。
1. 首先创建MySQL表结构
CREATE TABLE `cache_data` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`cache_key` varchar(255) NOT NULL COMMENT '缓存键',
`cache_value` longtext COMMENT '缓存值',
`expire_time` datetime DEFAULT NULL COMMENT '过期时间',
`created_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_cache_key` (`cache_key`),
KEY `idx_expire_time` (`expire_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='缓存数据表';
2. 实现MysqlCacheService工具类
package com.example.cache;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@Service
public class MysqlCacheService {
@Autowired
private JdbcTemplate jdbcTemplate;
private final ObjectMapper objectMapper = new ObjectMapper();
private static final String TABLE_NAME = "cache_data";
/**
* 设置缓存(永不过期)
*/
public void set(String key, Object value) {
set(key, value, null);
}
/**
* 设置缓存(带过期时间)
*/
public void set(String key, Object value, Long expireSeconds) {
try {
String jsonValue = objectMapper.writeValueAsString(value);
LocalDateTime expireTime = null;
if (expireSeconds != null && expireSeconds > 0) {
expireTime = LocalDateTime.now().plusSeconds(expireSeconds);
}
String sql = String.format(
"INSERT INTO %s (cache_key, cache_value, expire_time) VALUES (?, ?, ?) " +
"ON DUPLICATE KEY UPDATE cache_value = VALUES(cache_value), " +
"expire_time = VALUES(expire_time), updated_time = NOW()",
TABLE_NAME
);
jdbcTemplate.update(sql, key, jsonValue, expireTime);
} catch (Exception e) {
throw new RuntimeException("设置缓存失败: " + e.getMessage(), e);
}
}
/**
* 设置缓存(带过期时间和时间单位)
*/
public void set(String key, Object value, Long expire, TimeUnit timeUnit) {
if (expire != null && timeUnit != null) {
long expireSeconds = timeUnit.toSeconds(expire);
set(key, value, expireSeconds);
} else {
set(key, value);
}
}
/**
* 获取缓存
*/
public <T> T get(String key, Class<T> clazz) {
try {
// 先清理过期数据
cleanExpiredCache();
String sql = String.format(
"SELECT cache_value FROM %s WHERE cache_key = ? " +
"AND (expire_time IS NULL OR expire_time > NOW())",
TABLE_NAME
);
List<String> results = jdbcTemplate.queryForList(sql, String.class, key);
if (results.isEmpty()) {
return null;
}
String jsonValue = results.get(0);
if (!StringUtils.hasText(jsonValue)) {
return null;
}
return objectMapper.readValue(jsonValue, clazz);
} catch (Exception e) {
throw new RuntimeException("获取缓存失败: " + e.getMessage(), e);
}
}
/**
* 获取字符串缓存
*/
public String get(String key) {
return get(key, String.class);
}
/**
* 判断key是否存在
*/
public boolean exists(String key) {
cleanExpiredCache();
String sql = String.format(
"SELECT COUNT(1) FROM %s WHERE cache_key = ? " +
"AND (expire_time IS NULL OR expire_time > NOW())",
TABLE_NAME
);
Integer count = jdbcTemplate.queryForObject(sql, Integer.class, key);
return count != null && count > 0;
}
/**
* 删除缓存
*/
public boolean delete(String key) {
String sql = String.format("DELETE FROM %s WHERE cache_key = ?", TABLE_NAME);
int rows = jdbcTemplate.update(sql, key);
return rows > 0;
}
/**
* 批量删除缓存
*/
public int delete(String... keys) {
if (keys == null || keys.length == 0) {
return 0;
}
StringBuilder sql = new StringBuilder(String.format("DELETE FROM %s WHERE cache_key IN (", TABLE_NAME));
for (int i = 0; i < keys.length; i++) {
sql.append("?");
if (i < keys.length - 1) {
sql.append(",");
}
}
sql.append(")");
return jdbcTemplate.update(sql.toString(), (Object[]) keys);
}
/**
* 设置过期时间
*/
public boolean expire(String key, Long expireSeconds) {
LocalDateTime expireTime = LocalDateTime.now().plusSeconds(expireSeconds);
String sql = String.format(
"UPDATE %s SET expire_time = ?, updated_time = NOW() WHERE cache_key = ?",
TABLE_NAME
);
int rows = jdbcTemplate.update(sql, expireTime, key);
return rows > 0;
}
/**
* 获取剩余过期时间(秒)
*/
public Long ttl(String key) {
String sql = String.format(
"SELECT expire_time FROM %s WHERE cache_key = ?",
TABLE_NAME
);
List<LocalDateTime> results = jdbcTemplate.queryForList(sql, LocalDateTime.class, key);
if (results.isEmpty()) {
return -2L; // key不存在
}
LocalDateTime expireTime = results.get(0);
if (expireTime == null) {
return -1L; // 永不过期
}
LocalDateTime now = LocalDateTime.now();
if (expireTime.isBefore(now)) {
return 0L; // 已过期
}
return java.time.Duration.between(now, expireTime).getSeconds();
}
/**
* 清理过期缓存
*/
public int cleanExpiredCache() {
String sql = String.format(
"DELETE FROM %s WHERE expire_time IS NOT NULL AND expire_time <= NOW()",
TABLE_NAME
);
return jdbcTemplate.update(sql);
}
/**
* 获取所有匹配模式的key
*/
public List<String> keys(String pattern) {
cleanExpiredCache();
String sql = String.format(
"SELECT cache_key FROM %s WHERE cache_key LIKE ? " +
"AND (expire_time IS NULL OR expire_time > NOW())",
TABLE_NAME
);
String likePattern = pattern.replace("*", "%");
return jdbcTemplate.queryForList(sql, String.class, likePattern);
}
/**
* 清空所有缓存
*/
public int flushAll() {
String sql = String.format("DELETE FROM %s", TABLE_NAME);
return jdbcTemplate.update(sql);
}
/**
* 获取缓存统计信息
*/
public Map<String, Object> info() {
cleanExpiredCache();
String totalSql = String.format("SELECT COUNT(1) FROM %s", TABLE_NAME);
String activeSql = String.format(
"SELECT COUNT(1) FROM %s WHERE expire_time IS NULL OR expire_time > NOW()",
TABLE_NAME
);
Integer total = jdbcTemplate.queryForObject(totalSql, Integer.class);
Integer active = jdbcTemplate.queryForObject(activeSql, Integer.class);
return Map.of(
"total_keys", total != null ? total : 0,
"active_keys", active != null ? active : 0,
"expired_keys", (total != null ? total : 0) - (active != null ? active : 0)
);
}
}
3. 使用示例
@RestController
@RequestMapping("/cache")
public class CacheController {
@Autowired
private MysqlCacheService cacheService;
@PostMapping("/set")
public String setCache(@RequestParam String key,
@RequestParam String value,
@RequestParam(required = false) Long expireSeconds) {
cacheService.set(key, value, expireSeconds);
return "设置成功";
}
@GetMapping("/get")
public String getCache(@RequestParam String key) {
return cacheService.get(key);
}
@DeleteMapping("/delete")
public String deleteCache(@RequestParam String key) {
boolean success = cacheService.delete(key);
return success ? "删除成功" : "删除失败";
}
@GetMapping("/ttl")
public Long getTtl(@RequestParam String key) {
return cacheService.ttl(key);
}
@PostMapping("/expire")
public String setExpire(@RequestParam String key,
@RequestParam Long seconds) {
boolean success = cacheService.expire(key, seconds);
return success ? "设置过期时间成功" : "设置失败";
}
@GetMapping("/info")
public Map<String, Object> getCacheInfo() {
return cacheService.info();
}
}
4. 配置定时清理任务
@Component
public class CacheCleanupTask {
@Autowired
private MysqlCacheService cacheService;
@Scheduled(fixedRate = 300000) // 每5分钟执行一次
public void cleanupExpiredCache() {
int cleaned = cacheService.cleanExpiredCache();
if (cleaned > 0) {
System.out.println("清理过期缓存: " + cleaned + " 条");
}
}
}
5. 主要特性
✅ 基本操作:增删改查
✅ 过期时间:支持设置TTL
✅ 自动清理:定时清理过期数据
✅ 批量操作:支持批量删除
✅ 模式匹配:支持通配符查询
✅ 统计信息:提供缓存统计
✅ JSON序列化:支持复杂对象存储
✅ 线程安全:基于数据库事务保证一致性
这个实现提供了类似Redis的API接口,但数据持久化在MySQL中,适合对数据持久性要求较高的场景。