Redis 学习教程
目录
- 1. Redis 简介
- 2. Redis 安装部署
- 3. Redis 常用命令
- 4. Redis 数据类型详解
- 5. Java 操作 Redis
- 6. Redis 实战示例
- 7. Redis 持久化
- 8. Redis 高级特性
- 9. Redis 常见问题
1. Redis 简介
Redis(Remote Dictionary Server)是一个开源的内存数据结构存储系统,可以用作数据库、缓存和消息中间件。
1.1 Redis 特点
- 高性能: 读写速度可达 10万次/秒
- 丰富的数据类型: String、List、Set、Hash、ZSet 等
- 持久化: 支持 RDB 和 AOF 两种持久化方式
- 主从复制: 支持数据备份
- 高可用: 支持 Redis Cluster 和 Sentinel
- 单线程模型: 避免线程切换和锁竞争
1.2 Redis 应用场景
- 缓存: 会话缓存、页面缓存
- 排行榜: 利用 ZSet 实现
- 计数器: 文章点赞数、浏览量
- 分布式锁: 利用 SETNX 实现
- 消息队列: 利用 List 的 Pub/Sub
- 社交网络: 共同好友、关注列表
- 限流: 滑动窗口算法
2. Redis 安装部署
2.1 Linux/Mac 安装
方式一:使用包管理器安装
# Mac 使用 Homebrew
brew install redis
# Ubuntu/Debian
sudo apt-get update
sudo apt-get install redis-server
# CentOS/RHEL
sudo yum install redis
方式二:源码编译安装
# 1. 下载 Redis
wget https://download.redis.io/redis-stable.tar.gz
# 2. 解压
tar xzf redis-stable.tar.gz
cd redis-stable
# 3. 编译安装
make
make test
sudo make install
# 4. 启动 Redis
redis-server
2.2 Docker 安装
# 拉取 Redis 镜像
docker pull redis:latest
# 运行 Redis 容器
docker run -d \
--name my-redis \
-p 6379:6379 \
-v /data/redis:/data \
redis:latest \
redis-server --appendonly yes
# 带密码运行
docker run -d \
--name my-redis \
-p 6379:6379 \
redis:latest \
redis-server --requirepass "your-password"
2.3 Windows 安装
Redis 官方不支持 Windows,可以使用以下方式:
- 使用 WSL (Windows Subsystem for Linux)
- 使用 Docker Desktop
- 下载非官方编译版本
2.4 Redis 配置文件
配置文件位置:/etc/redis/redis.conf
# 绑定 IP
bind 127.0.0.1
# 端口
port 6379
# 守护进程
daemonize yes
# 数据库数量
databases 16
# RDB 持久化
save 900 1
save 300 10
save 60 10000
# AOF 持久化
appendonly yes
appendfsync everysec
# 密码
requirepass yourpassword
# 最大内存
maxmemory 256mb
maxmemory-policy allkeys-lru
2.5 Redis 启动与管理
# 启动 Redis
redis-server /path/to/redis.conf
# 连接 Redis
redis-cli
# 带密码连接
redis-cli -a yourpassword
# 远程连接
redis-cli -h 127.0.0.1 -p 6379 -a yourpassword
# 测试连接
redis-cli ping
# 返回: PONG
# 关闭 Redis
redis-cli shutdown
3. Redis 常用命令
3.1 基础命令
# 查看所有键
KEYS *
# 查看键数量
DBSIZE
# 查看键类型
TYPE keyname
# 检查键是否存在
EXISTS keyname
# 删除键
DEL keyname
# 设置过期时间(秒)
EXPIRE keyname 10
# 查看剩余时间
TTL keyname
# 重命名键
RENAME oldkey newkey
# 选择数据库
SELECT index
# 清空当前数据库
FLUSHDB
# 清空所有数据库
FLUSHALL
# 查看数据库信息
INFO
3.2 String 字符串操作
# 设置值
SET key value
# 获取值
GET key
# 设置值并指定过期时间(秒)
SETEX key seconds value
# 设置多个值
MSET key1 value1 key2 value2
# 获取多个值
MGET key1 key2
# 自增
INCR key
# 自增指定值
INCRBY key increment
# 自减
DECR key
# 追加字符串
APPEND key value
# 获取字符串长度
STRLEN key
3.3 List 列表操作
# 左侧插入
LPUSH key value1 value2
# 右侧插入
RPUSH key value1 value2
# 获取列表范围
LRANGE key 0 -1
# 左侧弹出
LPOP key
# 右侧弹出
RPOP key
# 获取列表长度
LLEN key
# 移除指定值
LREM key count value
# 按索引获取元素
LINDEX key index
# 设置索引元素
LSET key index value
# 保留指定范围
LTRIM key start stop
3.4 Set 集合操作
# 添加元素
SADD key member1 member2
# 获取所有元素
SMEMBERS key
# 检查元素是否存在
SISMEMBER key member
# 获取集合元素个数
SCARD key
# 删除元素
SREM key member
# 随机弹出元素
SPOP key
# 集合交集
SINTER set1 set2
# 集合并集
SUNION set1 set2
# 集合差集
SDIFF set1 set2
3.5 Hash 哈希操作
# 设置字段值
HSET key field value
# 获取字段值
HGET key field
# 设置多个字段
HMSET key field1 value1 field2 value2
# 获取多个字段
HMGET key field1 field2
# 获取所有字段
HGETALL key
# 获取所有字段名
HKEYS key
# 获取所有值
HVALS key
# 删除字段
HDEL key field
# 检查字段是否存在
HEXISTS key field
# 获取字段数量
HLEN key
# 字段值自增
HINCRBY key field increment
3.6 ZSet 有序集合操作
# 添加成员
ZADD key score member
# 获取排名范围
ZRANGE key start stop
# 带分数获取
ZRANGE key start stop WITHSCORES
# 按分数范围获取
ZRANGEBYSCORE key min max
# 获取成员分数
ZSCORE key member
# 获取成员排名
ZRANK key member
# 增加分数
ZINCRBY key increment member
# 删除成员
ZREM key member
# 获取集合大小
ZCARD key
4. Redis 数据类型详解
4.1 String(字符串)
应用场景: 缓存、计数器、分布式锁、Session共享
# 缓存用户信息
SET user:1001 '{"id":1001,"name":"张三"}'
# 计数器
INCR article:readcount:1001
# 分布式锁
SET lock:resource "uuid" NX EX 10
4.2 Hash(哈希)
应用场景: 对象存储、购物车、用户信息
# 存储用户信息
HSET user:1001 name "张三" age 25 email "zhangsan@example.com"
# 购物车
HADD cart:1001 product:1001 2
HADD cart:1001 product:1002 1
4.3 List(列表)
应用场景: 消息队列、文章列表、最新动态
# 消息队列(左侧生产,右侧消费)
LPUSH queue:email '{"to":"user@example.com","subject":"Hello"}'
RPOP queue:email
# 最新动态
LPUSH news:user:1001 "发布了新文章"
LRANGE news:user:1001 0 9
4.4 Set(集合)
应用场景: 标签、共同好友、抽奖
# 用户标签
SADD tags:user:1001 "java" "redis" "mysql"
# 共同好友
SINTER friends:user:1001 friends:user:1002
# 抽奖
SADD lottery:user:1001 user1001 user1002 user1003
SPOP lottery:user:1001
4.5 ZSet(有序集合)
应用场景: 排行榜、优先队列、延时队列
# 排行榜
ZADD rank:score 100 user1001 95 user1002 88 user1003
ZREVRANGE rank:score 0 9 WITHSCORES
# 延时队列
ZADD delay:queue 1709251200 "task1"
5. Java 操作 Redis
5.1 添加依赖
使用 Spring Boot + Jedis/Lettuce:
<!-- pom.xml -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
</dependencies>
5.2 配置 Redis
# application.yml
spring:
redis:
host: 127.0.0.1
port: 6379
password: yourpassword
database: 0
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: -1ms
5.3 Redis 配置类
package com.example.redis.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
// Key 使用 String 序列化
StringRedisSerializer stringSerializer = new StringRedisSerializer();
template.setKeySerializer(stringSerializer);
template.setHashKeySerializer(stringSerializer);
// Value 使用 JSON 序列化
Jackson2JsonRedisSerializer<Object> jsonSerializer =
new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.activateDefaultTyping(
LaissezFaireSubTypeValidator.instance,
ObjectMapper.DefaultTyping.NON_FINAL
);
jsonSerializer.setObjectMapper(mapper);
template.setValueSerializer(jsonSerializer);
template.setHashValueSerializer(jsonSerializer);
template.afterPropertiesSet();
return template;
}
}
5.4 Redis 工具类
package com.example.redis.util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@Component
public class RedisUtil {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// ==================== String 操作 ====================
public void set(String key, Object value) {
redisTemplate.opsForValue().set(key, value);
}
public void set(String key, Object value, long timeout, TimeUnit unit) {
redisTemplate.opsForValue().set(key, value, timeout, unit);
}
public Object get(String key) {
return redisTemplate.opsForValue().get(key);
}
public boolean del(String key) {
return Boolean.TRUE.equals(redisTemplate.delete(key));
}
public long del(Collection<String> keys) {
Long count = redisTemplate.delete(keys);
return count != null ? count : 0;
}
public boolean expire(String key, long timeout, TimeUnit unit) {
return Boolean.TRUE.equals(redisTemplate.expire(key, timeout, unit));
}
public long getExpire(String key) {
Long expire = redisTemplate.getExpire(key);
return expire != null ? expire : -1;
}
public boolean hasKey(String key) {
return Boolean.TRUE.equals(redisTemplate.hasKey(key));
}
public long incr(String key) {
Long count = redisTemplate.opsForValue().increment(key);
return count != null ? count : 0;
}
public long incrBy(String key, long delta) {
Long count = redisTemplate.opsForValue().increment(key, delta);
return count != null ? count : 0;
}
// ==================== Hash 操作 ====================
public void hSet(String key, String field, Object value) {
redisTemplate.opsForHash().put(key, field, value);
}
public Object hGet(String key, String field) {
return redisTemplate.opsForHash().get(key, field);
}
public void hSetAll(String key, Map<String, Object> map) {
redisTemplate.opsForHash().putAll(key, map);
}
public Map<Object, Object> hGetAll(String key) {
return redisTemplate.opsForHash().entries(key);
}
public void hDel(String key, Object... fields) {
redisTemplate.opsForHash().delete(key, fields);
}
public boolean hHasKey(String key, String field) {
return redisTemplate.opsForHash().hasKey(key, field);
}
public long hIncrBy(String key, String field, long delta) {
Long count = redisTemplate.opsForHash().increment(key, field, delta);
return count != null ? count : 0;
}
// ==================== List 操作 ====================
public long lPush(String key, Object value) {
Long count = redisTemplate.opsForList().leftPush(key, value);
return count != null ? count : 0;
}
public long lPushAll(String key, Collection<Object> values) {
Long count = redisTemplate.opsForList().leftPushAll(key, values);
return count != null ? count : 0;
}
public Object lPop(String key) {
return redisTemplate.opsForList().leftPop(key);
}
public long rPush(String key, Object value) {
Long count = redisTemplate.opsForList().rightPush(key, value);
return count != null ? count : 0;
}
public Object rPop(String key) {
return redisTemplate.opsForList().rightPop(key);
}
public List<Object> lRange(String key, long start, long end) {
return redisTemplate.opsForList().range(key, start, end);
}
public long lLen(String key) {
Long count = redisTemplate.opsForList().size(key);
return count != null ? count : 0;
}
// ==================== Set 操作 ====================
public long sAdd(String key, Object... values) {
Long count = redisTemplate.opsForSet().add(key, values);
return count != null ? count : 0;
}
public Set<Object> sMembers(String key) {
return redisTemplate.opsForSet().members(key);
}
public boolean sIsMember(String key, Object value) {
return Boolean.TRUE.equals(redisTemplate.opsForSet().isMember(key, value));
}
public long sSize(String key) {
Long count = redisTemplate.opsForSet().size(key);
return count != null ? count : 0;
}
public long sRem(String key, Object... values) {
Long count = redisTemplate.opsForSet().remove(key, values);
return count != null ? count : 0;
}
// ==================== ZSet 操作 ====================
public boolean zAdd(String key, Object value, double score) {
return Boolean.TRUE.equals(redisTemplate.opsForZSet().add(key, value, score));
}
public long zRem(String key, Object... values) {
Long count = redisTemplate.opsForZSet().remove(key, values);
return count != null ? count : 0;
}
public Set<Object> zRange(String key, long start, long end) {
return redisTemplate.opsForZSet().range(key, start, end);
}
public Set<Object> zReverseRange(String key, long start, long end) {
return redisTemplate.opsForZSet().reverseRange(key, start, end);
}
public long zRank(String key, Object value) {
Long rank = redisTemplate.opsForZSet().rank(key, value);
return rank != null ? rank : -1;
}
public double zScore(String key, Object value) {
Double score = redisTemplate.opsForZSet().score(key, value);
return score != null ? score : 0;
}
public long zSize(String key) {
Long count = redisTemplate.opsForZSet().size(key);
return count != null ? count : 0;
}
}
6. Redis 实战示例
6.1 缓存实现
package com.example.redis.service;
import com.example.redis.util.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class CacheService {
@Autowired
private RedisUtil redisUtil;
// 缓存查询
public <T> T get(String key, Class<T> clazz) {
Object value = redisUtil.get(key);
if (value != null) {
return (T) value;
}
return null;
}
// 缓存写入
public void set(String key, Object value) {
redisUtil.set(key, value, 30, TimeUnit.MINUTES);
}
// 缓存写入(指定过期时间)
public void set(String key, Object value, long timeout, TimeUnit unit) {
redisUtil.set(key, value, timeout, unit);
}
// 删除缓存
public void delete(String key) {
redisUtil.del(key);
}
}
6.2 分布式锁实现
package com.example.redis.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class DistributedLockService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final String LOCK_PREFIX = "lock:";
/**
* 尝试获取锁
* @param lockKey 锁的key
* @param requestId 请求标识(用于释放锁验证)
* @param expireTime 过期时间(秒)
* @return 是否获取成功
*/
public boolean tryLock(String lockKey, String requestId, long expireTime) {
String key = LOCK_PREFIX + lockKey;
Boolean result = redisTemplate.opsForValue()
.setIfAbsent(key, requestId, expireTime, TimeUnit.SECONDS);
return Boolean.TRUE.equals(result);
}
/**
* 释放锁
* @param lockKey 锁的key
* @param requestId 请求标识
*/
public void unlock(String lockKey, String requestId) {
String key = LOCK_PREFIX + lockKey;
Object value = redisTemplate.opsForValue().get(key);
// 验证是否是自己的锁
if (requestId.equals(value)) {
redisTemplate.delete(key);
}
}
/**
* 使用示例
*/
public void businessMethod() {
String lockKey = "resource:001";
String requestId = UUID.randomUUID().toString();
try {
// 尝试获取锁,过期时间30秒
if (tryLock(lockKey, requestId, 30)) {
// 执行业务逻辑
System.out.println("执行业务逻辑");
} else {
System.out.println("获取锁失败,业务繁忙");
}
} finally {
// 释放锁
unlock(lockKey, requestId);
}
}
}
6.3 限流实现
package com.example.redis.service;
import com.example.redis.util.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class RateLimitService {
@Autowired
private RedisUtil redisUtil;
/**
* 滑动窗口限流
* @param key 限流key
* @param limit 限流次数
* @param timeout 时间窗口(秒)
* @return 是否允许访问
*/
public boolean isAllowed(String key, int limit, long timeout) {
String currentKey = "ratelimit:" + key;
// 当前请求数+1
long current = redisUtil.incr(currentKey);
// 第一次请求时设置过期时间
if (current == 1) {
redisUtil.expire(currentKey, timeout, TimeUnit.SECONDS);
}
// 判断是否超过限流
return current <= limit;
}
}
6.4 排行榜实现
package com.example.redis.service;
import com.example.redis.util.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@Service
public class RankingService {
@Autowired
private RedisUtil redisUtil;
private static final String RANKING_KEY = "ranking:score";
/**
* 增加分数
*/
public void addScore(String userId, double score) {
redisUtil.zAdd(RANKING_KEY, userId, score);
}
/**
* 增加分数(增量)
*/
public void incrementScore(String userId, double delta) {
double currentScore = redisUtil.zScore(RANKING_KEY, userId);
redisUtil.zAdd(RANKING_KEY, userId, currentScore + delta);
}
/**
* 获取排行榜(前N名)
*/
public List<Map<String, Object>> getTopRanking(int topN) {
Set<Object> set = redisUtil.zReverseRange(RANKING_KEY, 0, topN - 1);
return set.stream()
.map(userId -> {
Map<String, Object> map = Map.of(
"userId", userId,
"score", redisUtil.zScore(RANKING_KEY, userId),
"rank", redisUtil.zRank(RANKING_KEY, userId) + 1
);
return map;
})
.collect(Collectors.toList());
}
/**
* 获取用户排名
*/
public long getUserRank(String userId) {
return redisUtil.zRank(RANKING_KEY, userId) + 1;
}
/**
* 获取用户分数
*/
public double getUserScore(String userId) {
return redisUtil.zScore(RANKING_KEY, userId);
}
}
6.5 购物车实现
package com.example.redis.service;
import com.example.redis.util.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
@Service
public class ShoppingCartService {
@Autowired
private RedisUtil redisUtil;
/**
* 添加商品到购物车
*/
public void addCartItem(String userId, String productId, int quantity) {
String key = "cart:" + userId;
redisUtil.hSet(key, productId, quantity);
}
/**
* 修改商品数量
*/
public void updateCartItem(String userId, String productId, int quantity) {
String key = "cart:" + userId;
if (quantity <= 0) {
redisUtil.hDel(key, productId);
} else {
redisUtil.hSet(key, productId, quantity);
}
}
/**
* 删除商品
*/
public void removeCartItem(String userId, String productId) {
String key = "cart:" + userId;
redisUtil.hDel(key, productId);
}
/**
* 获取购物车所有商品
*/
public Map<String, Integer> getCartItems(String userId) {
String key = "cart:" + userId;
Map<Object, Object> rawMap = redisUtil.hGetAll(key);
Map<String, Integer> cartItems = new HashMap<>();
rawMap.forEach((k, v) -> {
cartItems.put(k.toString(), Integer.valueOf(v.toString()));
});
return cartItems;
}
/**
* 清空购物车
*/
public void clearCart(String userId) {
String key = "cart:" + userId;
redisUtil.del(key);
}
/**
* 获取购物车商品数量
*/
public int getCartItemCount(String userId) {
String key = "cart:" + userId;
Map<Object, Object> rawMap = redisUtil.hGetAll(key);
return rawMap.values().stream()
.mapToInt(v -> Integer.valueOf(v.toString()))
.sum();
}
}
6.6 消息队列实现
package com.example.redis.service;
import com.example.redis.util.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class MessageQueueService {
@Autowired
private RedisUtil redisUtil;
/**
* 发送消息
*/
public void sendMessage(String queueName, Object message) {
String key = "queue:" + queueName;
redisUtil.lPush(key, message);
}
/**
* 批量发送消息
*/
public void sendMessages(String queueName, List<Object> messages) {
String key = "queue:" + queueName;
redisUtil.lPushAll(key, messages);
}
/**
* 接收消息
*/
public Object receiveMessage(String queueName) {
String key = "queue:" + queueName;
return redisUtil.rPop(key);
}
/**
* 批量接收消息
*/
public List<Object> receiveMessages(String queueName, int count) {
String key = "queue:" + queueName;
List<Object> messages = new ArrayList<>();
for (int i = 0; i < count; i++) {
Object message = redisUtil.rPop(key);
if (message == null) {
break;
}
messages.add(message);
}
return messages;
}
/**
* 获取队列长度
*/
public long getQueueSize(String queueName) {
String key = "queue:" + queueName;
return redisUtil.lLen(key);
}
}
7. Redis 持久化
7.1 RDB(快照持久化)
RDB 是在指定的时间间隔内生成数据集的时间点快照。
优点:
- 紧凑的单一文件
- 恢复速度快
- 适合备份
缺点:
- 可能丢失最后一次持久后的数据
- fork 子进程时可能阻塞
配置示例:
save 900 1 # 900秒内至少有1个key变化
save 300 10 # 300秒内至少有10个key变化
save 60 10000 # 60秒内至少有10000个key变化
rdbcompression yes # 压缩RDB文件
rdbfilename dump.rdb # RDB文件名
dir /var/lib/redis # 文件目录
7.2 AOF(追加文件持久化)
AOF 记录服务器执行的所有写操作命令。
优点:
- 数据安全性高
- 可读性强,易于修复
缺点:
- 文件体积大
- 恢复速度较慢
配置示例:
appendonly yes # 开启AOF
appendfilename "appendonly.aof"
appendfsync everysec # 每秒同步一次
no-appendfsync-on-rewrite no
AOF 重写:
auto-aof-rewrite-percentage 100 # AOF文件比上次重写增长100%时触发
auto-aof-rewrite-min-size 64mb # AOF文件至少64MB时触发
8. Redis 高级特性
8.1 发布订阅
# 订阅频道
SUBSCRIBE channel1
# 发布消息
PUBLISH channel1 "hello"
# 取消订阅
UNSUBSCRIBE channel1
8.2 事务
# 开启事务
MULTI
# 执行命令
SET key1 value1
SET key2 value2
# 执行事务
EXEC
# 取消事务
DISCARD
8.3 Lua 脚本
# 执行脚本
EVAL "return redis.call('SET', KEYS[1], ARGV[1])" 1 mykey myvalue
# 加载脚本
SCRIPT LOAD "return redis.call('GET', KEYS[1])"
# 执行已加载脚本
EVALSHA sha值 1 mykey
8.4 Pipeline
// Java Pipeline 示例
public void pipelineExample() {
redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
for (int i = 0; i < 10000; i++) {
connection.set(("key:" + i).getBytes(), ("value:" + i).getBytes());
}
return null;
});
}
9. Redis 常见问题
9.1 缓存穿透
问题: 查询不存在的数据,导致每次都查数据库
解决方案:
- 缓存空值
- 布隆过滤器
- 请求参数校验
public Object getByCache(String key) {
Object value = redisUtil.get(key);
if (value != null) {
return "NULL".equals(value) ? null : value;
}
// 查询数据库
value = database.query(key);
if (value == null) {
// 缓存空值
redisUtil.set(key, "NULL", 5, TimeUnit.MINUTES);
} else {
redisUtil.set(key, value, 30, TimeUnit.MINUTES);
}
return value;
}
9.2 缓存击穿
问题: 热点key过期,大量请求直接打到数据库
解决方案:
- 热点数据永不过期
- 互斥锁
- 异步刷新
public Object getWithLock(String key) {
Object value = redisUtil.get(key);
if (value != null) {
return value;
}
String lockKey = "lock:" + key;
try {
// 获取锁
if (distributedLock.tryLock(lockKey, requestId, 10)) {
// 双重检查
value = redisUtil.get(key);
if (value != null) {
return value;
}
// 查询数据库
value = database.query(key);
redisUtil.set(key, value, 30, TimeUnit.MINUTES);
return value;
}
} finally {
distributedLock.unlock(lockKey, requestId);
}
// 获取锁失败,稍后重试或返回默认值
return null;
}
9.3 缓存雪崩
问题: 大量key同时过期,数据库压力骤增
解决方案:
- 过期时间加随机值
- 多级缓存
- 限流降级
// 设置过期时间时加随机值
int randomTime = RandomUtil.randomInt(5, 15);
redisUtil.set(key, value, 30 + randomTime, TimeUnit.MINUTES);
9.4 内存淘汰策略
# 配置淘汰策略
maxmemory 256mb
maxmemory-policy allkeys-lru
淘汰策略说明:
noeviction: 不淘汰,写入报错allkeys-lru: 淘汰最少使用的keyallkeys-lfu: 淘汰使用频率最低的keyallkeys-random: 随机淘汰volatile-lru: 淘汰设置了TTL中最少使用的volatile-lfu: 淘汰设置了TTL中使用频率最低的volatile-random: 随机淘汰设置了TTL的volatile-ttl: 淘汰即将过期的
10. 总结
本教程涵盖了 Redis 的核心内容:
- 安装部署: Linux/Mac/Windows/Docker 多种安装方式
- 基础命令: 5种数据类型的常用操作
- Java 集成: Spring Boot + RedisTemplate 完整配置
- 实战应用: 缓存、分布式锁、限流、排行榜、购物车、消息队列
- 高级特性: 持久化、发布订阅、事务、Lua脚本
- 问题解决: 缓存穿透、击穿、雪崩的经典解决方案
学习建议
- 先掌握基础命令和数据类型
- 动手实践每个 Java 示例
- 理解 Redis 的应用场景
- 学习 Redis 的高可用方案(主从、哨兵、集群)
- 关注性能优化和监控
推荐资源
- 官方文档: https://redis.io/docs/
- 在线测试: https://try.redis.io/
- 命令参考: https://redis.io/commands/


被折叠的 条评论
为什么被折叠?



