Redis 从入门到实战
一、Redis 是什么?
Redis(Remote Dictionary Server)本质是:
一个 基于内存的高性能 Key-Value 数据库
你可以把它理解成:
text
Redis ≈ 一个超快的 HashMap(但功能远不止)
Redis 的核心特点
- 超高性能(10万+ QPS)
- 基于内存(读写极快)
- 支持持久化(不会完全丢数据)
- 支持丰富数据结构
- 支持分布式(集群)
二、Redis 为什么这么快?(面试高频)
核心原因 5 点:
1️⃣ 基于内存
不走磁盘 IO,速度直接起飞
2️⃣ 单线程模型(重点)
Redis 是单线程,但它:
避免了线程切换开销 + 锁竞争
3️⃣ IO 多路复用
底层用类似:
- epoll(Linux)
- select
一个线程处理多个连接
4️⃣ 数据结构简单高效
后面会讲(核心)
5️⃣ C语言实现
更贴近底层,性能更高
三、Redis 核心数据结构(重点中的重点)
Redis ≠ 只会 String!
它有 5 大核心结构:
1️⃣ String(最常用)
bash
SET name "Tom"
GET name
特点:
- 可以存字符串 / 数字 / JSON
- 最大 512MB
使用场景:
- 缓存
- 计数器(点赞数)
bash
INCR likes
2️⃣ Hash(对象存储)
bash
HSET user:1 name Tom age 20
HGET user:1 name
类似 Java:
java
Map<String, Object>
使用场景:
- 存用户信息
- 商品信息
3️⃣ List(列表)
bash
LPUSH list 1 2 3
RPOP list
特点:
- 有序
- 可重复
使用场景:
- 消息队列(简单版)
- 最新动态
4️⃣ Set(去重集合)
bash
SADD users 1 2 3
SMEMBERS users
特点:
- 无序
- 不重复
使用场景:
- 去重
- 标签系统
5️⃣ ZSet(有序集合 )
bash
ZADD score 100 user1
ZADD score 90 user2
ZREVRANGE score 0 1
特点:
- 自动排序(按 score)
使用场景:
- 排行榜(核心场景)
四、Redis 底层数据结构(进阶)
面试加分项!
| Redis类型 | 底层结构 |
|---|---|
| String | SDS(简单动态字符串) |
| List | quicklist |
| Hash | hashTable / ziplist |
| Set | hashTable |
| ZSet | 跳表(skiplist) |
什么是跳表(SkipList)?
类似"多层索引的链表"
优势:
- 查询效率接近二叉树
- 插入删除比红黑树简单
五、Redis 如何使用(实战)
1️⃣ 安装(简单了解)
Linux:
bash
apt install redis
启动:
bash
redis-server
客户端:
bash
redis-cli
2️⃣ Java 操作 Redis(重点)
常用客户端:
- Jedis
- Lettuce
- Spring Data Redis(最常用 )
Spring Boot 示例
依赖
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
配置
yaml
spring:
redis:
host: localhost
port: 6379
使用
java
@Autowired
private StringRedisTemplate redisTemplate;
public void test() {
redisTemplate.opsForValue().set("name", "Tom");
String name = redisTemplate.opsForValue().get("name");
}
六、Redis 常见使用场景(面试必问)
1️⃣ 缓存(最核心)
text
数据库 → Redis → 用户
大幅降低数据库压力
2️⃣ 分布式锁(高频)
bash
SET lock_key value NX EX 10
代码示例:
@Service
public class OrderService {
@Autowired
private StringRedisTemplate redisTemplate;
private static final String LOCK_KEY = "order:lock:";
private static final long LOCK_EXPIRE = 10L; // 秒
public boolean createOrder(String orderId) {
// 1. 尝试获取锁
String lockKey = LOCK_KEY + orderId;
String value = UUID.randomUUID().toString(); // 唯一值
// SET lock_key unique_value NX EX 10
Boolean success = redisTemplate.opsForValue()
.setIfAbsent(lockKey, value, LOCK_EXPIRE, TimeUnit.SECONDS);
if (Boolean.TRUE.equals(success)) {
try {
// 2. 获取锁成功,执行业务逻辑
return doCreateOrder(orderId);
} finally {
// 3. 释放锁(使用Lua脚本保证原子性)
String luaScript =
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
"return redis.call('del', KEYS[1]) " +
"else return 0 end";
redisTemplate.execute(
new DefaultRedisScript<>(luaScript, Long.class),
Collections.singletonList(lockKey),
value
);
}
}
// 获取锁失败
return false;
}
}
防止重复提交、库存超卖
3️⃣ 排行榜
ZSet 实现:
bash
ZADD rank 100 user1
代码示例:
@Service
public class RankService {
@Autowired
private RedisTemplate<String, String> redisTemplate;
private static final String RANK_KEY = "game:rank";
// 增加用户分数
public void addScore(String userId, double score) {
// ZADD rank 100 user1
redisTemplate.opsForZSet().add(RANK_KEY, userId, score);
}
// 获取Top N
public List<UserScore> getTopN(int n) {
// ZREVRANGE rank 0 n-1 WITHSCORES
Set<ZSetOperations.TypedTuple<String>> set =
redisTemplate.opsForZSet().reverseRangeWithScores(RANK_KEY, 0, n - 1);
List<UserScore> result = new ArrayList<>();
set.forEach(tuple -> {
result.add(new UserScore(tuple.getValue(), tuple.getScore()));
});
return result;
}
// 获取用户排名
public Long getUserRank(String userId) {
// ZREVRANK rank user1 (返回从0开始的排名)
return redisTemplate.opsForZSet().reverseRank(RANK_KEY, userId);
}
}
4️⃣ 计数器
bash
INCR page_view
代码示例:
@Service
public class CounterService {
@Autowired
private StringRedisTemplate redisTemplate;
// 文章阅读量+1
public Long incrementViewCount(Long articleId) {
String key = "article:view:" + articleId;
// INCR page_view
return redisTemplate.opsForValue().increment(key);
}
// 获取阅读量
public Long getViewCount(Long articleId) {
String key = "article:view:" + articleId;
String value = redisTemplate.opsForValue().get(key);
return value == null ? 0L : Long.parseLong(value);
}
// 批量增加(如点赞+10)
public Long incrementBy(Long articleId, long delta) {
String key = "article:like:" + articleId;
return redisTemplate.opsForValue().increment(key, delta);
}
}
5️⃣ 消息队列(简单)
List:
bash
LPUSH queue msg
RPOP queue
代码示例:
@Component
public class SimpleMessageQueue {
@Autowired
private StringRedisTemplate redisTemplate;
private static final String QUEUE_KEY = "task:queue";
// 生产者:发送消息
public void sendMessage(String message) {
// LPUSH queue msg
redisTemplate.opsForList().leftPush(QUEUE_KEY, message);
}
// 消费者:阻塞获取消息
public String receiveMessage() {
// BRPOP queue 0 (0表示无限等待)
List<String> result = redisTemplate.execute(
(RedisCallback<List<String>>) connection -> {
byte[][] keys = new byte[][]{QUEUE_KEY.getBytes()};
List<byte[]> bytesList = connection.bRPop(0, keys);
if (bytesList != null && !bytesList.isEmpty()) {
return bytesList.stream()
.map(String::new)
.collect(Collectors.toList());
}
return null;
}
);
return result != null ? result.get(1) : null; // result[0]=key, result[1]=value
}
// 非阻塞获取
public String popMessage() {
// RPOP queue
return redisTemplate.opsForList().rightPop(QUEUE_KEY);
}
}
七、Redis 易错点
1. 缓存穿透
查不存在的数据
解决:
- 布隆过滤器
- 缓存空值
2. 缓存击穿
热点 key 突然失效
解决:
- 加锁
- 永不过期 + 异步更新
3. 缓存雪崩
大量 key 同时过期
解决:
- 随机过期时间
- 多级缓存
4. 数据一致性问题
Redis 和数据库不一致
解决:
- 先更新数据库再删缓存(常用)
- 双删策略
八、Redis 持久化机制(面试重点)
1️⃣ RDB(快照)
定时保存数据
优点:
- 恢复快
缺点:
- 可能丢数据
2️⃣ AOF(日志)
记录每条命令
优点:
- 更安全
缺点:
- 文件大
面试回答模板
"生产一般 AOF + RDB 混合使用"
九、Redis 面试高频问题总结
1. Redis 为什么快?
内存 + 单线程 + IO多路复用
2. Redis 和 MySQL 区别?
| Redis | MySQL |
|---|---|
| 内存 | 磁盘 |
| 快 | 相对慢 |
| KV | 关系型 |
3. Redis 是线程安全吗?
单线程 → 天然线程安全
4. Redis 如何实现分布式锁?
SET NX EX
5. Redis 会丢数据吗?
会(看持久化策略)
十、总结(建议背下来)
Redis 核心可以总结为:
text
1. 五大数据结构(String/Hash/List/Set/ZSet)
2. 三大缓存问题(穿透/击穿/雪崩)
3. 两种持久化(RDB/AOF)
4. 一个核心优势(快)