Organized 50 interview questions into 12 categories: - 01-分布式系统 (9 files): 分布式事务, 分布式锁, 一致性哈希, CAP理论, etc. - 02-数据库 (2 files): MySQL索引优化, MyBatis核心原理 - 03-缓存 (5 files): Redis数据结构, 缓存问题, LRU算法, etc. - 04-消息队列 (1 file): RocketMQ/Kafka - 05-并发编程 (4 files): 线程池, 设计模式, 限流策略, etc. - 06-JVM (1 file): JVM和垃圾回收 - 07-系统设计 (8 files): 秒杀系统, 短链接, IM, Feed流, etc. - 08-算法与数据结构 (4 files): B+树, 红黑树, 跳表, 时间轮 - 09-网络与安全 (3 files): TCP/IP, 加密安全, 性能优化 - 10-中间件 (4 files): Spring Boot, Nacos, Dubbo, Nginx - 11-运维 (4 files): Kubernetes, CI/CD, Docker, 可观测性 - 12-面试技巧 (1 file): 面试技巧和职业规划 All files renamed to Chinese for better accessibility and organized into categorized folders for easier navigation. Generated with [Claude Code](https://claude.com/claude-code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
216 lines
4.5 KiB
Markdown
216 lines
4.5 KiB
Markdown
# Redis 数据结构
|
||
|
||
## 问题
|
||
|
||
1. Redis 有哪些数据结构?底层实现是什么?
|
||
2. String 类型的应用场景?
|
||
3. Hash 和 String 的区别?
|
||
4. List 的应用场景?
|
||
5. Set 和 ZSet 的区别?
|
||
6. Bitmap、HyperLogLog、GEO 的应用?
|
||
|
||
---
|
||
|
||
## 标准答案
|
||
|
||
### 1. Redis 数据类型
|
||
|
||
| 类型 | 底层实现 | 应用场景 |
|
||
|------|---------|----------|
|
||
| **String** | SDS | 缓存、计数器、分布式锁 |
|
||
| **Hash** | 压缩列表/哈希表 | 对象存储、购物车 |
|
||
| **List** | 双向链表/压缩列表 | 消息队列、最新列表 |
|
||
| **Set** | 哈希表/整数集合 | 标签、共同关注 |
|
||
| **ZSet** | 跳表/哈希表 | 排行榜、延时队列 |
|
||
| **Bitmap** | String(位操作) | 签到、在线用户 |
|
||
| **HyperLogLog** | String(基数统计) | UV 统计 |
|
||
| **GEO** | ZSet(经纬度编码) | 附近的人 |
|
||
|
||
---
|
||
|
||
### 2. String(SDS - Simple Dynamic String)
|
||
|
||
**结构**:
|
||
```c
|
||
struct sdshdr {
|
||
int len; // 已使用长度
|
||
int free; // 剩余空间
|
||
char buf[]; // 字节数组
|
||
};
|
||
```
|
||
|
||
**优势**:
|
||
- O(1) 获取长度
|
||
- 防止缓冲区溢出
|
||
- 减少内存分配次数
|
||
|
||
**应用**:
|
||
```bash
|
||
# 缓存
|
||
SET user:1001 '{"id":1001,"name":"Alice"}'
|
||
GET user:1001
|
||
|
||
# 计数器
|
||
INCR view_count:1001
|
||
DECR stock:1001
|
||
|
||
# 分布式锁
|
||
SET lock:order:1001 "uuid" NX PX 30000
|
||
```
|
||
|
||
---
|
||
|
||
### 3. Hash
|
||
|
||
**结构**:
|
||
```bash
|
||
HSET user:1001 name "Alice" age 25 email "alice@example.com"
|
||
HGET user:1001 name
|
||
HGETALL user:1001
|
||
```
|
||
|
||
**底层**:
|
||
- 字段少(< 512):压缩列表(ziplist)
|
||
- 字段多(≥ 512):哈希表(hashtable)
|
||
|
||
**应用**:
|
||
```java
|
||
// 对象存储(推荐 Hash,而非 String)
|
||
redisTemplate.opsForHash().putAll("user:1001", Map.of(
|
||
"name", "Alice",
|
||
"age", "25"
|
||
));
|
||
|
||
// 购物车
|
||
redisTemplate.opsForHash().put("cart:1001", "product:1001", "2");
|
||
```
|
||
|
||
---
|
||
|
||
### 4. List
|
||
|
||
**结构**:
|
||
```bash
|
||
LPUSH list:msgs "msg1" "msg2" "msg3" # 左侧插入
|
||
RPOP list:msgs # 右侧弹出
|
||
```
|
||
|
||
**底层**:
|
||
- 元素少(< 512):压缩列表(ziplist)
|
||
- 元素多(≥ 512):双向链表(linkedlist)
|
||
- Redis 3.2+:quicklist(ziplist + linkedlist)
|
||
|
||
**应用**:
|
||
```bash
|
||
# 消息队列
|
||
LPUSH queue:email '{"to":"alice@example.com","subject":"Hello"}'
|
||
RPOP queue:email
|
||
|
||
# 最新列表
|
||
LPUSH timeline:1001 "post1" "post2"
|
||
LRANGE timeline:1001 0 9 # 最新 10 条
|
||
```
|
||
|
||
---
|
||
|
||
### 5. Set vs ZSet
|
||
|
||
**Set(无序集合)**:
|
||
```bash
|
||
SADD tags:article:1001 "java" "redis" "mysql"
|
||
SMEMBERS tags:article:1001
|
||
SISMEMBER tags:article:1001 "java"
|
||
SINTER tags:user:1001 tags:article:1001 # 交集
|
||
```
|
||
|
||
**底层**:
|
||
- 元素少(< 512):整数集合(intset)
|
||
- 元素多(≥ 512):哈希表(hashtable)
|
||
|
||
---
|
||
|
||
**ZSet(有序集合)**:
|
||
```bash
|
||
ZADD rank:score 100 "player1" 200 "player2" 150 "player3"
|
||
ZREVRANGE rank:score 0 9 WITHSCORES # Top 10
|
||
ZRANK rank:score "player1" # 排名
|
||
ZSCORE rank:score "player1" # 分数
|
||
```
|
||
|
||
**底层**:
|
||
- 元素少(< 128):压缩列表(ziplist)
|
||
- 元素多(≥ 128):跳表(skiplist) + 哈希表(hashtable)
|
||
|
||
---
|
||
|
||
### 6. Bitmap
|
||
|
||
**原理**:用 bit 位表示状态(0 或 1)
|
||
|
||
```bash
|
||
# 签到
|
||
SETBIT sign:2024:02:28:1001 0 1 # 用户 1001 在第 0 天签到
|
||
SETBIT sign:2024:02:28:1001 4 1 # 用户 1001 在第 4 天签到
|
||
|
||
# 统计签到天数
|
||
BITCOUNT sign:2024:02:28:1001
|
||
|
||
# 用户 1001 和 1002 共同签到的天数
|
||
BITOP AND result sign:2024:02:28:1001 sign:2024:02:28:1002
|
||
BITCOUNT result
|
||
```
|
||
|
||
---
|
||
|
||
### 7. HyperLogLog
|
||
|
||
**用途**:基数统计(不重复元素个数)
|
||
|
||
**优点**:内存占用极小(12 KB)
|
||
|
||
```bash
|
||
PFADD uv:2024:02:28 user:1001 user:1002 user:1003
|
||
PFCOUNT uv:2024:02:28 # 3
|
||
|
||
# 合并多个 HyperLogLog
|
||
PFMERGE uv:2024:02:01-28 uv:2024:02:01 uv:2024:02:02 ...
|
||
```
|
||
|
||
**误差率**:< 1%
|
||
|
||
---
|
||
|
||
### 8. GEO(地理位置)
|
||
|
||
```bash
|
||
# 添加位置
|
||
GEOADD locations:users 116.404 39.915 "user:1001" # 北京
|
||
|
||
# 查找附近的人(5 km 内)
|
||
GEORADIUS locations:users 116.404 39.915 5 km
|
||
|
||
# 计算距离
|
||
GEODIST locations:users user:1001 user:1002
|
||
```
|
||
|
||
**底层**:ZSet(经纬度编码为 score)
|
||
|
||
---
|
||
|
||
### 9. 阿里 P7 加分项
|
||
|
||
**深度理解**:
|
||
- 理解 SDS 和 C 字符串的区别
|
||
- 理解跳表的实现原理
|
||
- 理解压缩列表的优缺点
|
||
|
||
**实战经验**:
|
||
- 有选择合适数据类型的经验
|
||
- 有大数据量下的优化经验
|
||
- 有 Redis 内存优化的经验
|
||
|
||
**架构能力**:
|
||
- 能设计基于 Redis 的业务方案
|
||
- 能设计 Redis 集群方案
|
||
- 能设计 Redis 监控体系
|