# 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 监控体系