目录
1. 什么是Redis
1.1 简单理解
想象一下你的大脑记忆系统:
短期记忆(Redis):
- 快速存取,瞬间想起
- 容量有限,但速度极快
- 用于存储最近使用的信息
长期记忆(数据库):
- 容量巨大,但访问较慢
- 需要时间去"回忆"
- 存储所有历史信息
1.2 技术定义
Redis(Remote Dictionary Server)是一个开源的、基于内存的数据结构存储系统,可以用作:
- 缓存:加速数据访问
- 数据库:持久化存储
- 消息队列:异步通信
1.3 核心特点
Redis的优势:
速度极快: 所有数据存储在内存中
数据结构丰富: 支持多种数据类型
持久化支持: 可以将数据保存到磁盘
集群支持: 支持分布式部署
原子操作: 保证数据一致性
生活类比:
- Redis就像便利店:东西不多但拿取很快
- 数据库就像大型超市:东西很全但需要找一会儿
2. Redis安装和配置
2.1 Docker安装(推荐)
最简单的安装方式:
# 1. 拉取Redis镜像
docker pull redis:7.0
# 2. 启动Redis容器
docker run -d \
--name redis-server \
-p 6379:6379 \
-v /data/redis:/data \
redis:7.0 redis-server --appendonly yes
# 3. 进入Redis客户端
docker exec -it redis-server redis-cli
# 4. 测试连接
127.0.0.1:6379> ping
PONG
2.2 Linux系统安装
# Ubuntu/Debian安装
sudo apt update
sudo apt install redis-server
# CentOS/RHEL安装
sudo yum install epel-release
sudo yum install redis
# 启动Redis服务
sudo systemctl start redis
sudo systemctl enable redis
# 检查服务状态
sudo systemctl status redis
2.3 基础配置
Redis配置文件(redis.conf)
# 基本配置
port 6379 # 端口号
bind 127.0.0.1 # 绑定IP地址
daemonize yes # 后台运行
# 内存配置
maxmemory 2gb # 最大内存限制
maxmemory-policy allkeys-lru # 内存满时的淘汰策略
# 持久化配置
save 900 1 # 900秒内至少1个key变化时保存
save 300 10 # 300秒内至少10个key变化时保存
save 60 10000 # 60秒内至少10000个key变化时保存
# AOF持久化
appendonly yes # 启用AOF持久化
appendfsync everysec # 每秒同步一次
# 安全配置
requirepass your_password # 设置密码
性能优化配置
# TCP配置
tcp-keepalive 300 # TCP保活时间
tcp-backlog 511 # TCP监听队列长度
# 客户端配置
timeout 0 # 客户端空闲超时时间(0表示永不超时)
maxclients 10000 # 最大客户端连接数
# 内存优化
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-size -2
set-max-intset-entries 512
3. Redis数据类型详解
3.1 String(字符串)
生活类比:就像在纸条上写字,一张纸条存一个信息。
# 基本操作
SET name "张三" # 设置值
GET name # 获取值
DEL name # 删除
# 带过期时间
SETEX session_id 3600 "abc123" # 设置并指定3600秒过期
TTL session_id # 查看剩余过期时间
# 数字操作
SET counter 1
INCR counter # 自增1,结果为2
INCRBY counter 5 # 增加5,结果为7
DECR counter # 自减1,结果为6
Java代码示例
@Service
public class StringRedisService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 基本字符串操作
*/
public void stringOperations() {
// 设置值
redisTemplate.opsForValue().set("user:name", "张三");
// 设置带过期时间的值
redisTemplate.opsForValue().set("session:123", "login_token", 30, TimeUnit.MINUTES);
// 获取值
String name = (String) redisTemplate.opsForValue().get("user:name");
System.out.println("用户名: " + name);
// 检查key是否存在
Boolean exists = redisTemplate.hasKey("user:name");
System.out.println("key存在: " + exists);
// 设置过期时间
redisTemplate.expire("user:name", 1, TimeUnit.HOURS);
}
/**
* 数字操作
*/
public void numberOperations() {
// 计数器操作
redisTemplate.opsForValue().set("page_views", 0);
// 增加访问量
Long views = redisTemplate.opsForValue().increment("page_views");
System.out.println("页面访问量: " + views);
// 增加指定数量
Long newViews = redisTemplate.opsForValue().increment("page_views", 10);
System.out.println("新的访问量: " + newViews);
}
/**
* 批量操作
*/
public void batchOperations() {
// 批量设置
Map<String, Object> values = new HashMap<>();
values.put("user:1:name", "张三");
values.put("user:1:age", 25);
values.put("user:1:city", "北京");
redisTemplate.opsForValue().multiSet(values);
// 批量获取
List<String> keys = Arrays.asList("user:1:name", "user:1:age", "user:1:city");
List<Object> results = redisTemplate.opsForValue().multiGet(keys);
for (int i = 0; i < keys.size(); i++) {
System.out.println(keys.get(i) + ": " + results.get(i));
}
}
}
3.2 Hash(哈希)
生活类比:就像一个联系人卡片,一张卡片上有姓名、电话、地址等多个字段。
# 基本操作
HSET user:1 name "张三" age 25 city "北京" # 设置多个字段
HGET user:1 name # 获取单个字段
HMGET user:1 name age city # 获取多个字段
HGETALL user:1 # 获取所有字段
HDEL user:1 city # 删除字段
Java代码示例
@Service
public class HashRedisService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 用户信息管理
*/
public void userManagement() {
String userKey = "user:1001";
// 设置用户信息
Map<String, Object> userInfo = new HashMap<>();
userInfo.put("name", "李四");
userInfo.put("age", 28);
userInfo.put("email", "[email protected]");
userInfo.put("phone", "13812345678");
userInfo.put("department", "技术部");
redisTemplate.opsForHash().putAll(userKey, userInfo);
// 设置单个字段
redisTemplate.opsForHash().put(userKey, "last_login", new Date().toString());
// 获取单个字段
String name = (String) redisTemplate.opsForHash().get(userKey, "name");
System.out.println("用户姓名: " + name);
// 获取多个字段
List<Object> fields = redisTemplate.opsForHash().multiGet(userKey,
Arrays.asList("name", "email", "department"));
System.out.println("用户信息: " + fields);
// 获取所有字段
Map<Object, Object> allInfo = redisTemplate.opsForHash().entries(userKey);
allInfo.forEach((key, value) -> {
System.out.println(key + ": " + value);
});
// 检查字段是否存在
Boolean hasEmail = redisTemplate.opsForHash().hasKey(userKey, "email");
System.out.println("有邮箱信息: " + hasEmail);
// 获取所有字段名
Set<Object> keys = redisTemplate.opsForHash().keys(userKey);
System.out.println("所有字段: " + keys);
// 删除字段
redisTemplate.opsForHash().delete(userKey, "phone");
}
/**
* 商品信息管理示例
*/
public void productManagement() {
String productKey = "product:2001";
// 商品基本信息
redisTemplate.opsForHash().put(productKey, "name", "iPhone 15");
redisTemplate.opsForHash().put(productKey, "price", "6999.00");
redisTemplate.opsForHash().put(productKey, "category", "手机");
redisTemplate.opsForHash().put(productKey, "brand", "Apple");
redisTemplate.opsForHash().put(productKey, "stock", "100");
// 价格操作(数字字段)
redisTemplate.opsForHash().increment(productKey, "stock", -1); // 库存减1
// 获取库存
Object stock = redisTemplate.opsForHash().get(productKey, "stock");
System.out.println("当前库存: " + stock);
// 获取商品详情
Map<Object, Object> productInfo = redisTemplate.opsForHash().entries(productKey);
System.out.println("商品信息: " + productInfo);
}
}
3.3 List(列表)
生活类比:就像购物清单或待办事项,有顺序,可以从头部或尾部添加删除。
# 基本操作
LPUSH shopping_list "苹果" "香蕉" # 从左边添加
RPUSH shopping_list "牛奶" # 从右边添加
LPOP shopping_list # 从左边弹出
RPOP shopping_list # 从右边弹出
LLEN shopping_list # 获取长度
LRANGE shopping_list 0 -1 # 获取所有元素
Java代码示例
@Service
public class ListRedisService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 消息队列示例
*/
public void messageQueue() {
String queueKey = "message_queue";
// 生产者:发送消息(从左边推入)
redisTemplate.opsForList().leftPush(queueKey, "消息1: 用户注册成功");
redisTemplate.opsForList().leftPush(queueKey, "消息2: 订单支付完成");
redisTemplate.opsForList().leftPush(queueKey, "消息3: 商品库存告警");
System.out.println("队列长度: " + redisTemplate.opsForList().size(queueKey));
// 消费者:处理消息(从右边弹出)
while (redisTemplate.opsForList().size(queueKey) > 0) {
String message = (String) redisTemplate.opsForList().rightPop(queueKey);
System.out.println("处理消息: " + message);
}
}
/**
* 最近访问记录
*/
public void recentVisitedPages() {
String userKey = "user:1001:recent_pages";
// 记录访问页面(保持最新的10个页面)
String[] pages = {"首页", "商品列表", "商品详情", "购物车", "订单页面"};
for (String page : pages) {
// 添加到列表头部
redisTemplate.opsForList().leftPush(userKey, page);
// 保持列表长度不超过10
redisTemplate.opsForList().trim(userKey, 0, 9);
}
// 获取最近访问的页面
List<Object> recentPages = redisTemplate.opsForList().range(userKey, 0, -1);
System.out.println("最近访问页面: " + recentPages);
// 获取最新访问的3个页面
List<Object> top3Pages = redisTemplate.opsForList().range(userKey, 0, 2);
System.out.println("最新3个页面: " + top3Pages);
}
/**
* 文章评论管理
*/
public void articleComments() {
String articleKey = "article:1001:comments";
// 添加评论(按时间顺序)
redisTemplate.opsForList().rightPush(articleKey, "这篇文章写得很好!");
redisTemplate.opsForList().rightPush(articleKey, "学到了很多知识");
redisTemplate.opsForList().rightPush(articleKey, "期待更多这样的文章");
redisTemplate.opsForList().rightPush(articleKey, "作者辛苦了");
// 获取评论总数
Long commentCount = redisTemplate.opsForList().size(articleKey);
System.out.println("评论总数: " + commentCount);
// 分页获取评论(每页3条)
int page = 1;
int pageSize = 3;
long start = (page - 1) * pageSize;
long end = start + pageSize - 1;
List<Object> comments = redisTemplate.opsForList().range(articleKey, start, end);
System.out.println("第" + page + "页评论: " + comments);
// 获取最新的评论
Object latestComment = redisTemplate.opsForList().index(articleKey, -1);
System.out.println("最新评论: " + latestComment);
}
/**
* 阻塞队列操作
*/
public void blockingQueue() {
String queueKey = "blocking_queue";
// 在另一个线程中消费消息
new Thread(() -> {
while (true) {
try {
// 阻塞式弹出(等待10秒)
List<Object> result = redisTemplate.opsForList().rightPop(queueKey, 10, TimeUnit.SECONDS);
if (result != null && !result.isEmpty()) {
System.out.println("消费消息: " + result.get(1));
} else {
System.out.println("等待超时,没有新消息");
}
} catch (Exception e) {
System.out.println("消费异常: " + e.getMessage());
break;
}
}
}).start();
// 生产消息
try {
Thread.sleep(2000);
redisTemplate.opsForList().leftPush(queueKey, "延迟消息1");
Thread.sleep(5000);
redisTemplate.opsForList().leftPush(queueKey, "延迟消息2");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
3.4 Set(集合)
生活类比:就像朋友圈,每个人只能出现一次,可以找共同好友。
# 基本操作
SADD tags "Java" "Redis" "Spring" # 添加元素
SMEMBERS tags # 获取所有元素
SISMEMBER tags "Java" # 检查元素是否存在
SCARD tags # 获取元素数量
SREM tags "Redis" # 删除元素
# 集合运算
SINTER set1 set2 # 交集
SUNION set1 set2 # 并集
SDIFF set1 set2 # 差集
Java代码示例
@Service
public class SetRedisService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 用户标签管理
*/
public void userTags() {
String userKey = "user:1001:tags";
// 添加用户标签
redisTemplate.opsForSet().add(userKey, "Java开发者", "技术爱好者", "游戏玩家", "美食达人");
// 检查是否有某个标签
Boolean hasTag = redisTemplate.opsForSet().isMember(userKey, "Java开发者");
System.out.println("是Java开发者: " + hasTag);
// 获取所有标签
Set<Object> allTags = redisTemplate.opsForSet().members(userKey);
System.out.println("用户标签: " + allTags);
// 获取标签数量
Long tagCount = redisTemplate.opsForSet().size(userKey);
System.out.println("标签数量: " + tagCount);
// 随机获取一个标签
Object randomTag = redisTemplate.opsForSet().randomMember(userKey);
System.out.println("随机标签: " + randomTag);
// 删除标签
redisTemplate.opsForSet().remove(userKey, "游戏玩家");
}
/**
* 共同好友功能
*/
public void commonFriends() {
// 用户A的好友
String userAFriends = "user:A:friends";
redisTemplate.opsForSet().add(userAFriends, "张三", "李四", "王五", "赵六");
// 用户B的好友
String userBFriends = "user:B:friends";
redisTemplate.opsForSet().add(userBFriends, "李四", "王五", "钱七", "孙八");
// 计算共同好友(交集)
Set<Object> commonFriends = redisTemplate.opsForSet().intersect(userAFriends, userBFriends);
System.out.println("A和B的共同好友: " + commonFriends);
// 计算A独有的好友(差集)
Set<Object> uniqueFriendsA = redisTemplate.opsForSet().difference(userAFriends, userBFriends);
System.out.println("A独有的好友: " + uniqueFriendsA);
// 计算所有好友(并集)
Set<Object> allFriends = redisTemplate.opsForSet().union(userAFriends, userBFriends);
System.out.println("所有好友: " + allFriends);
// 保存共同好友到新的集合
String commonKey = "user:A:B:common_friends";
redisTemplate.opsForSet().intersectAndStore(userAFriends, userBFriends, commonKey);
}
/**
* 文章标签系统
*/
public void articleTagSystem() {
// 文章1的标签
String article1Tags = "article:1001:tags";
redisTemplate.opsForSet().add(article1Tags, "Java", "Spring Boot", "Redis", "教程");
// 文章2的标签
String article2Tags = "article:1002:tags";
redisTemplate.opsForSet().add(article2Tags, "Python", "Django", "Redis", "Web开发");
// 根据标签推荐相关文章
Set<Object> relatedTags = redisTemplate.opsForSet().intersect(article1Tags, article2Tags);
System.out.println("相关标签: " + relatedTags);
// 按标签分类文章
String redisTagArticles = "tag:Redis:articles";
redisTemplate.opsForSet().add(redisTagArticles, "article:1001", "article:1002");
// 获取Redis标签下的所有文章
Set<Object> redisArticles = redisTemplate.opsForSet().members(redisTagArticles);
System.out.println("Redis相关文章: " + redisArticles);
}
/**
* 去重和抽奖功能
*/
public void deduplicationAndLottery() {
String participantsKey = "lottery:participants";
// 添加参与者(自动去重)
redisTemplate.opsForSet().add(participantsKey,
"user1001", "user1002", "user1003", "user1001", "user1004", "user1002");
System.out.println("参与者数量: " + redisTemplate.opsForSet().size(participantsKey));
// 随机抽取中奖者
Set<Object> winners = redisTemplate.opsForSet().distinctRandomMembers(participantsKey, 2);
System.out.println("中奖者: " + winners);
// 弹出式抽奖(中奖后移除)
Object luckyWinner = redisTemplate.opsForSet().pop(participantsKey);
System.out.println("幸运大奖得主: " + luckyWinner);
System.out.println("剩余参与者: " + redisTemplate.opsForSet().size(participantsKey));
}
}
3.5 Sorted Set(有序集合)
生活类比:就像排行榜,每个人都有分数,按分数排序。
# 基本操作
ZADD leaderboard 100 "张三" 95 "李四" 88 "王五" # 添加带分数的元素
ZRANGE leaderboard 0 -1 WITHSCORES # 按分数升序获取
ZREVRANGE leaderboard 0 -1 WITHSCORES # 按分数降序获取
ZSCORE leaderboard "张三" # 获取某个元素的分数
ZRANK leaderboard "张三" # 获取排名(升序)
ZREVRANK leaderboard "张三" # 获取排名(降序)
Java代码示例
@Service
public class SortedSetRedisService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 游戏排行榜
*/
public void gameLeaderboard() {
String leaderboardKey = "game:leaderboard";
// 添加玩家分数
redisTemplate.opsForZSet().add(leaderboardKey, "张三", 1500);
redisTemplate.opsForZSet().add(leaderboardKey, "李四", 1200);
redisTemplate.opsForZSet().add(leaderboardKey, "王五", 1800);
redisTemplate.opsForZSet().add(leaderboardKey, "赵六", 1350);
redisTemplate.opsForZSet().add(leaderboardKey, "钱七", 1650);
// 获取排行榜前3名(分数降序)
Set<ZSetOperations.TypedTuple<Object>> top3 =
redisTemplate.opsForZSet().reverseRangeWithScores(leaderboardKey, 0, 2);
System.out.println("=== 游戏排行榜前3名 ===");
int rank = 1;
for (ZSetOperations.TypedTuple<Object> entry : top3) {
System.out.println("第" + rank + "名: " + entry.getValue() + " - " + entry.getScore() + "分");
rank++;
}
// 获取某个玩家的排名和分数
String playerName = "张三";
Double score = redisTemplate.opsForZSet().score(leaderboardKey, playerName);
Long ranking = redisTemplate.opsForZSet().reverseRank(leaderboardKey, playerName);
System.out.println(playerName + " 的分数: " + score + ", 排名: " + (ranking + 1));
// 增加分数
Double newScore = redisTemplate.opsForZSet().incrementScore(leaderboardKey, playerName, 100);
System.out.println(playerName + " 获得100分奖励,新分数: " + newScore);
// 获取分数范围内的玩家
Set<Object> middlePlayers = redisTemplate.opsForZSet().rangeByScore(leaderboardKey, 1300, 1600);
System.out.println("分数在1300-1600之间的玩家: " + middlePlayers);
// 获取总玩家数
Long totalPlayers = redisTemplate.opsForZSet().zCard(leaderboardKey);
System.out.println("总玩家数: " + totalPlayers);
}