2.Redis7核心数据结构

Redis7 核心数据结构

    • [📋 知识体系总览](#📋 知识体系总览)
    • [Redis7 数据结构全景](#Redis7 数据结构全景)
      • [✅ Redis7 有哪些数据结构](#✅ Redis7 有哪些数据结构)
      • [✅ 永远的神:help 命令](#✅ 永远的神:help 命令)
    • 一、核心数据结构
      • [✅1. String 字符串](#✅1. String 字符串)
      • [✅2. Hash 哈希](#✅2. Hash 哈希)
        • [Hash 常用操作](#Hash 常用操作)
        • [String vs Hash 存储对象对比](#String vs Hash 存储对象对比)
        • [Hash 应用场景](#Hash 应用场景)
        • [Hash 结构优缺点](#Hash 结构优缺点)
      • [✅3. List 列表](#✅3. List 列表)
        • [List 常用操作](#List 常用操作)
        • [List 应用场景](#List 应用场景)
        • [List 注意事项](#List 注意事项)
      • [✅4. Set 集合](#✅4. Set 集合)
        • [Set 常用操作](#Set 常用操作)
        • [Set 运算操作](#Set 运算操作)
        • [Set 应用场景](#Set 应用场景)
      • [✅5. ZSet 有序集合](#✅5. ZSet 有序集合)
        • [ZSet 常用操作](#ZSet 常用操作)
        • [ZSet 集合运算](#ZSet 集合运算)
        • [ZSet 应用场景:排行榜](#ZSet 应用场景:排行榜)
    • 二、扩展数据结构
      • [✅6. Bitmap 位图](#✅6. Bitmap 位图)
        • [Bitmap 常用操作](#Bitmap 常用操作)
        • [Bitmap 应用场景:每日签到](#Bitmap 应用场景:每日签到)
      • [✅7. HyperLogLog](#✅7. HyperLogLog)
        • [HyperLogLog 常用操作](#HyperLogLog 常用操作)
        • 使用示例
      • [✅8. Geo 地理位置](#✅8. Geo 地理位置)
        • [Geo 常用操作](#Geo 常用操作)
        • [Geo 应用场景:附近商家推荐](#Geo 应用场景:附近商家推荐)
    • 三、高级数据结构
      • [✅9. Stream 消息流](#✅9. Stream 消息流)
        • [Stream 常用操作](#Stream 常用操作)
        • [Stream 消费者组操作](#Stream 消费者组操作)
    • [四、SpringBoot 集成 Redis](#四、SpringBoot 集成 Redis)
      • [✅10. Maven 依赖与配置](#✅10. Maven 依赖与配置)
      • [✅11. RestTemplate 快速上手](#✅11. RestTemplate 快速上手)
      • [✅12. RedisTemplate 解决中文乱码](#✅12. RedisTemplate 解决中文乱码)
    • [📋 全文总结](#📋 全文总结)
      • [✅1. 数据结构总览对比](#✅1. 数据结构总览对比)
      • [✅2. 面试高频考点](#✅2. 面试高频考点)
      • [✅3. 数据结构选择决策树](#✅3. 数据结构选择决策树)

📋 知识体系总览

复制代码
Redis7 核心数据结构
├── 数据结构全景
│   ├── ✅ 核心版 vs 扩展版
│   └── ✅ help 命令
├── 一、核心数据结构
│   ├── ✅1. String 字符串
│   ├── ✅2. Hash 哈希
│   ├── ✅3. List 列表
│   ├── ✅4. Set 集合
│   └── ✅5. ZSet 有序集合
├── 二、扩展数据结构
│   ├── ✅6. Bitmap 位图
│   ├── ✅7. HyperLogLog
│   └── ✅8. Geo 地理位置
├── 三、高级数据结构
│   └── ✅9. Stream 消息流
└── 四、SpringBoot 集成 Redis
    ├── ✅10. Maven 依赖与配置
    ├── ✅11. RestTemplate 快速上手
    └── ✅12. RedisTemplate 中文乱码解决

Redis7 数据结构全景

✅ Redis7 有哪些数据结构

Redis7 的数据结构分为核心版扩展版两大类别:

✅ 永远的神:help 命令

在 Redis 中,help 命令是学习数据结构的最佳入口,通过 help @<group> 可以查看任意数据结构的完整命令列表。

📝 面试要点: 面试官常问"Redis有哪些数据结构?",标准回答是:5 种核心数据结构(String、Hash、List、Set、ZSet)+ 4 种扩展数据结构(Bitmap、HyperLogLog、Geo、Stream)。Bitmap 本质是 String 的位操作,Geo 基于 ZSet 实现。

一、核心数据结构

✅1. String 字符串

📝 关键理解: String 是 Redis 中最基础的数据类型,其 value 根据字符串格式不同分为 3 类:string(普通字符串)、int(整数类型)、float(浮点类型),其中 int 和 float 可以做自增自减操作。底层是动态字符串(SDS)。最大容量 512MB。

字符串常用操作
命令 说明
SET key value 存入字符串键值对(key 不存在则新增,存在则修改)
GET key 获取一个字符串键值
MSET key value [key value ...] 批量存储字符串键值对
MGET key [key ...] 批量获取字符串键值
SETNX key value 存入一个不存在的字符串键值对
SETEX key seconds value 存入字符串键值对并指定有效期(秒)
DEL key [key ...] 删除一个键
EXPIRE key seconds 设置一个键的过期时间(秒)
APPEND key value 向字符串末尾追加内容

使用示例:

bash 复制代码
127.0.0.1:6379> set name Rose     # key 不存在则新增
OK
127.0.0.1:6379> get name
"Rose"
127.0.0.1:6379> set name Jack     # key 已存在则修改
OK
127.0.0.1:6379> get name
"Jack"

127.0.0.1:6379> MSET k1 v1 k2 v2 k3 v3
OK
127.0.0.1:6379> MGET name k1 k2 k3
1) "Jack"
2) "v1"
3) "v2"
4) "v3"
原子加减操作
命令 说明
INCR key 将 key 中储存的数字值加 1
DECR key 将 key 中储存的数字值减 1
INCRBY key increment 将 key 所储存的值加上 increment(负数则减)
DECRBY key decrement 将 key 所储存的值减去 decrement

使用示例:

bash 复制代码
127.0.0.1:6379> set age 10
OK
127.0.0.1:6379> incr age          # 自增1
(integer) 11
127.0.0.1:6379> incrby age 2      # 自增2
(integer) 13
127.0.0.1:6379> incrby age -1     # 加负数 = 减
(integer) 12
127.0.0.1:6379> decr age          # 自减1
(integer) 11
Key 的层级结构

Redis 没有类似 MySQL 的 Table 概念,如何区分不同类型的 key 呢?可以通过给 key 添加前缀,用 : 分隔多个单词,形成层级结构:

复制代码
多级 key 格式:项目名:业务名:类型:id

例如:

  • user 相关:test:user:1
  • product 相关:test:product:1

如果 Value 是一个 Java 对象,可以将对象序列化为 JSON 字符串后存储:

复制代码
test:user:1  →  {"id":1, "name": "Jack", "age": 21}
test:product:1 → {"id":1, "name": "小米11", "price": 4999}

在可视化界面中,Redis 会以层级结构来展示,更加直观方便管理。

String 常见应用场景

① 单值缓存:

bash 复制代码
SET key value
GET key

② 对象缓存:

bash 复制代码
-- 方式一:JSON 字符串(简单,但不能单独操作字段)
SET user:1 '{"name":"roy","balance":1888}'

-- 方式二:多键存储(推荐,便于单独操作每个属性)
MSET user:1:name roy user:1:balance 1888
MGET user:1:name user:1:balance

③ 分布式锁:

bash 复制代码
SETNX product:10001 true          # 返回1=获取锁成功,返回0=失败
-- 执行业务操作
DEL product:10001                 # 执行完业务释放锁

-- 推荐写法(原子操作:加锁 + 设置过期时间,防止死锁)
SET product:10001 true EX 10 NX

✅2. Hash 哈希

📝 关键理解: Hash 类型,也叫散列,其 value 是一个无序字典,类似于 Java 中的 HashMap 结构。适合存储对象,将一个对象的多个属性存储在一个 key 下,可以针对单个 field 做 CRUD。底层使用 listpack(小数据量)或 hashtable(大数据量)。

Hash 常用操作
命令 说明
HSET key field value 存储一个哈希表 key 的键值(field 不存在则新增,存在则修改)
HGET key field 获取哈希表 key 对应的 field 键值
HSETNX key field value 存储一个不存在的哈希表 key 的键值
HMSET key field value [field value ...] 在一个哈希表 key 中存储多个键值对
HMGET key field [field ...] 批量获取哈希表 key 中多个 field 键值
HGETALL key 返回哈希表 key 中所有的 field 和 value
HKEYS key 获取一个 hash 类型的 key 中的所有 field
HVALS key 获取一个 hash 类型的 key 中的所有 value
HDEL key field [field ...] 删除哈希表 key 中的 field 键值
HLEN key 返回哈希表 key 中 field 的数量
HINCRBY key field increment 为哈希表 key 中 field 键的值加上增量 increment

使用示例:

bash 复制代码
127.0.0.1:6379> HSET heima:user:3 name Lucy   # field 不存在则新增
(integer) 1
127.0.0.1:6379> HSET heima:user:3 age 21
(integer) 1
127.0.0.1:6379> HSET heima:user:3 age 17      # field 存在则修改
(integer) 0
127.0.0.1:6379> HGET heima:user:3 name
"Lucy"
127.0.0.1:6379> HGET heima:user:3 age
"17"

127.0.0.1:6379> HMSET heima:user:4 name LiLei age 20 sex man
OK
127.0.0.1:6379> HMGET heima:user:4 name age sex
1) "LiLei"
2) "20"
3) "man"

127.0.0.1:6379> HGETALL heima:user:4
1) "name"
2) "LiLei"
3) "age"
4) "20"
5) "sex"
6) "man"

127.0.0.1:6379> HKEYS heima:user:4
1) "name"
2) "age"
3) "sex"

127.0.0.1:6379> HINCRBY heima:user:4 age 2    # age 自增2
(integer) 22
String vs Hash 存储对象对比
方式 写法 优点 缺点
String(JSON) SET user:1 '{"name":"Jack","age":21}' 简单直接 修改单个字段需反序列化整个 JSON
String(多键) MSET user:1:name Jack user:1:age 21 可单独操作每个属性 key 数量多,管理不便
Hash HSET user:1 name Jack age 21 归类整合、节省空间、可单独操作 field field 不支持独立设置过期时间
Hash 应用场景

① 对象缓存:

bash 复制代码
HSET user:1 name roy balance 1888
HMGET user:1 name balance

② 电商购物车:

  • 用户 id 为 key
  • 商品 id 为 field
  • 商品数量为 value
bash 复制代码
-- 添加商品
HSET cart:1001 10088 1

-- 增加数量
HINCRBY cart:1001 10088 1

-- 商品总数
HLEN cart:1001

-- 删除商品
HDEL cart:1001 10088

-- 获取购物车所有商品
HGETALL cart:1001
Hash 结构优缺点
优点 缺点
同类数据归类整合储存,方便数据管理 过期功能不能使用在 field 上,只能用在 key 上
相比 string 操作消耗内存与 CPU 更小 Redis 集群架构下不适合大规模使用
相比 string 储存更节省空间 ---

✅3. List 列表

📝 关键理解: List 与 Java 中的 LinkedList 类似,底层是一个双向链表。支持正向和反向检索。特征:有序、元素可重复、插入删除快、查询速度一般。一个 list 的容量是 2^32-1 个元素(约 40 多亿)。使用时要注意大 key 问题。

List 常用操作
命令 说明
LPUSH key value [value ...] 将一个或多个值插入到 key 列表的表头(最左边)
RPUSH key value [value ...] 将一个或多个值插入到 key 列表的表尾(最右边)
LPOP key 移除并返回 key 列表的头元素,列表为空则返回 nil
RPOP key 移除并返回 key 列表的尾元素,列表为空则返回 nil
LRANGE key start stop 返回列表 key 中指定区间内的元素(-1 表示到末尾)
BLPOP key [key ...] timeout 从表头弹出元素,若无元素则阻塞等待 timeout 秒(timeout=0 一直阻塞)
BRPOP key [key ...] timeout 从表尾弹出元素,若无元素则阻塞等待 timeout 秒(timeout=0 一直阻塞)

使用示例:

bash 复制代码
127.0.0.1:6379> LPUSH users 1 2 3      # 左进:3 2 1
(integer) 3
127.0.0.1:6379> RPUSH users 4 5 6      # 右进:3 2 1 4 5 6
(integer) 6
127.0.0.1:6379> LPOP users             # 左出:弹出3
"3"
127.0.0.1:6379> RPOP users             # 右出:弹出6
"6"
127.0.0.1:6379> LRANGE users 0 -1      # 查看所有元素
1) "2"
2) "1"
3) "4"
4) "5"
List 应用场景

常用数据结构组合:

复制代码
Stack(栈)            = LPUSH + LPOP
Queue(队列)          = LPUSH + RPOP
Blocking MQ(阻塞队列)  = LPUSH + BRPOP

常见业务场景:

  • 视频列表、签到列表
  • 排队机
  • 简化版的 MQ(消息队列)
List 注意事项
  1. 一个 list 的容量是 2^32-1 个元素,大概 40 多亿。但在应用时,要注意大 key 问题
  2. list 的底层是一个双向链表,对双端的操作性能很高。但是通过索引下标直接操作某一个中间节点的性能就会比较低。

✅4. Set 集合

📝 关键理解: Set 是无序不重复集合,底层使用 listpack(小数据量)或 hashtable。支持交并差集运算,适合做标签、点赞、抽奖等场景。

Set 常用操作
命令 说明
SADD key member [member ...] 往集合 key 中存入元素,元素存在则忽略,key 不存在则新建
SREM key member [member ...] 从集合 key 中删除元素
SMEMBERS key 获取集合 key 中所有元素
SCARD key 获取集合 key 的元素个数
SISMEMBER key member 判断 member 元素是否存在于集合 key 中
SRANDMEMBER key [count] 从集合 key 中选出 count 个元素,元素不从 key 中删除
SPOP key [count] 从集合 key 中选出 count 个元素,元素从 key 中删除
Set 运算操作
命令 说明
SINTER key [key ...] 交集运算
SINTERSTORE destination key [key ..] 将交集结果存入新集合 destination 中
SUNION key [key ..] 并集运算
SUNIONSTORE destination key [key ...] 将并集结果存入新集合 destination 中
SDIFF key [key ...] 差集运算
SDIFFSTORE destination key [key ...] 将差集结果存入新集合 destination 中
Set 应用场景

① 微信抽奖小程序:

bash 复制代码
-- 点击参与抽奖加入集合
SADD key {userID}

-- 查看参与抽奖所有用户
SMEMBERS key

-- 抽取 count 名中奖者
SRANDMEMBER key [count]     -- 不删除元素,可重复中奖
SPOP key [count]            -- 删除元素,不可重复中奖

② 微信/微博点赞、收藏、标签:

bash 复制代码
-- 点赞
SADD like:{消息ID} {用户ID}

-- 取消点赞
SREM like:{消息ID} {用户ID}

-- 检查用户是否点过赞
SISMEMBER like:{消息ID} {用户ID}

-- 获取点赞的用户列表
SMEMBERS like:{消息ID}

-- 获取点赞用户数
SCARD like:{消息ID}

③ 集合运算实现社交关系:

bash 复制代码
SINTER set1 set2    → 共同关注的人
SUNION set1 set2    → 朋友圈所有人
SDIFF set1 set2     → 可能认识的人(推荐好友)

④ 社交好友关系实战练习:

需求:

  • 张三的好友有:李四、王五、赵六
  • 李四的好友有:王五、麻子、二狗
bash 复制代码
127.0.0.1:6379> SADD zs lisi wangwu zhaoliu
(integer) 3
127.0.0.1:6379> SADD ls wangwu mazi ergou
(integer) 3

-- 计算张三好友有几人
127.0.0.1:6379> SCARD zs
(integer) 3

-- 计算张三和李四的共同好友
127.0.0.1:6379> SINTER zs ls
1) "wangwu"

-- 查询哪些人是张三好友却不是李四好友
127.0.0.1:6379> SDIFF zs ls
1) "zhaoliu"
2) "lisi"

-- 查询张三和李四的全部好友
127.0.0.1:6379> SUNION zs ls
1) "wangwu"
2) "zhaoliu"
3) "lisi"
4) "mazi"
5) "ergou"

-- 判断李四是否是张三的好友
127.0.0.1:6379> SISMEMBER zs lisi
(integer) 1               # 返回1:是好友

-- 将李四从张三的好友列表中移除
127.0.0.1:6379> SREM zs lisi
(integer) 1

✅5. ZSet 有序集合

📝 关键理解: ZSet 与 Java 中的 TreeSet 类似,每个元素关联一个 score 用于排序。底层使用 listpack(小数据量)+ skiplist(跳跃表)+ hashtable。可排序、元素不重复、查询速度快。适合排行榜场景。

ZSet 常用操作
命令 说明
ZADD key score member [[score member]...] 往有序集合 key 中加入带分值元素(已存在则更新 score)
ZREM key member [member ...] 从有序集合 key 中删除元素
ZSCORE key member 返回有序集合 key 中元素 member 的分值
ZRANK key member 获取指定元素的排名(从 0 开始,按分数从小到大)
ZREVRANK key member 获取指定元素的排名(从 0 开始,按分数从大到小)
ZCARD key 返回有序集合 key 中元素个数
ZCOUNT key min max 统计有序集合中分数在给定范围内的元素个数
ZINCRBY key increment member 为有序集合 key 中元素 member 的分值加上 increment
ZRANGE key start stop [WITHSCORES] 正序获取有序集合 key 从 start 下标到 stop 下标的元素
ZREVRANGE key start stop [WITHSCORES] 倒序获取有序集合 key 从 start 下标到 stop 下标的元素
ZRANGEBYSCORE key min max [WITHSCORES] 按 score 排序后,获取指定 score 范围内的元素

📝 注意: 所有排名默认都是升序,如果要降序则在命令的 Z 后面加 REV。例如:ZRANK(升序排名)→ ZREVRANK(降序排名);ZRANGE(升序获取)→ ZREVRANGE(降序获取)。

ZSet 集合运算
命令 说明
ZUNIONSTORE destkey numkeys key [key ...] 并集计算
ZINTERSTORE destkey numkeys key [key ...] 交集计算
ZDIFFSTORE destkey numkeys key [key ...] 差集计算
ZSet 应用场景:排行榜
bash 复制代码
-- 1)用户点击新闻,增加热度
ZINCRBY hotNews:20190819 1 守护香港

-- 2)展示当日排行前十(按热度倒序)
ZREVRANGE hotNews:20190819 0 9 WITHSCORES

-- 3)七日搜索榜单计算(合并7天的数据)
ZUNIONSTORE hotNews:20190813-20190819 7 \
  hotNews:20190813 hotNews:20190814 ... hotNews:20190819

-- 4)展示七日排行前十
ZREVRANGE hotNews:20190813-20190819 0 9 WITHSCORES

二、扩展数据结构

✅6. Bitmap 位图

📝 关键理解: Bitmap 本质是 String 的位操作,用一个 bit 位来表示某个元素对应的值或状态。最大优势是节省空间,适合只有两个状态的数据统计。

Bitmap 常用操作
命令 说明
SETBIT key offset value 将一个二进制数组的 offset 位置设置成 value(0 或 1)
GETBIT key offset 返回一个二进制数组的 offset 位置的值
`BITCOUNT key [start end [BYTE BIT]]`
`BITPOS key bit [start [end [BYTE BIT]]]`
`BITOP AND OR
Bitmap 应用场景:每日签到
bash 复制代码
-- 1号用户第100天完成了签到
SETBIT dailycheck:1 100 1

-- 统计1号用户的签到次数
BITCOUNT dailycheck:1

-- 统计1号用户第一天签到的时间
BITPOS dailycheck:1 1

优点:快速、高效、节省空间。

✅7. HyperLogLog

📝 关键理解: HyperLogLog 用于统计一个集合中不重复的元素个数(基数统计)。它不保存具体数据,占用空间极小(12KB),但有约 0.81% 的误差率。典型应用场景:统计网站的 UV。

HyperLogLog 常用操作
命令 说明
PFADD key element [element ...] 添加元素到 HyperLogLog
PFCOUNT key [key ...] 返回 HyperLogLog 的基数估算值
PFMERGE destkey sourcekey [sourcekey ...] 将多个 HyperLogLog 合并为一个
使用示例
bash 复制代码
-- 添加用户访问记录
PFADD visitlog 192.168.65.111 192.168.65.112 192.168.65.111

-- 统计不同的独立访客(UV)
PFCOUNT visitlog

📝 面试要点: HyperLogLog 和 Set 都可以做去重统计,但 HyperLogLog 不存储原始数据,内存占用仅 12KB,适合超大数据量的去重统计(如亿级 UV)。Set 精确但占内存,适合小数据量精确去重。

✅8. Geo 地理位置

📝 关键理解: Geo 基于 ZSet 实现,用于存储地理位置信息(经纬度),支持距离计算、附近搜索等。底层使用 GeoHash 算法将二维坐标编码为一个字符串。

Geo 常用操作
命令 说明
`GEOADD key [NX XX] [CH] longitude latitude member [...]`
GEOPOS key [member [member ...]] 返回地址的经纬度
`GEODIST key member1 member2 [M KM
`GEORADIUS key longitude latitude radius M KM
`GEOSEARCH key FROMMEMBER member FROMLONLAT longitude latitude BYRADIUS radius ...`
Geo 应用场景:附近商家推荐

获取经纬度坐标:https://api.map.baidu.com/lbsapi/getpoint/index.html

bash 复制代码
-- 添加商家地址
GEOADD changsha 113.017489 28.200454 火车站 \
                112.96903 28.201195 橘子洲 \
                113.017031 28.199706 赛格广场 \
                113.017004 28.197677 国储

-- 查询两个地点之间的距离
GEODIST changsha 火车站 橘子洲 M

-- 查找火车站附近 2KM 内的景点
GEORADIUSBYMEMBER changsha 火车站 2 KM WITHdist WITHcoord COUNT 4 WITHhash

三、高级数据结构

✅9. Stream 消息流

📝 关键理解: Stream 是 Redis 版的 MQ(消息队列),结合了阻塞队列 + pub/sub 的特性。优点是轻量级、低延迟,但企业应用中较少直接使用 Redis Stream,更多选择专业的 MQ 如 Kafka、RocketMQ 等。了解即可。

Stream 常用操作
命令 说明
`XADD key [NOMKSTREAM] [MAXLEN MINID ...] *
XDEL key id [id ...] 删除队列中的一条消息
XLEN key 获取队列的长度
XRANGE key start end [COUNT count] 查询队列中的消息
Stream 消费者组操作
bash 复制代码
-- 创建队列,并添加消息(*表示让系统自动生成ID)
XADD mystream * name loulan name roy name admin

-- 查看队列消息(- 队列开始,+ 队列结尾)
XRANGE mystream - +

-- 创建消费者组(0 从队列头部开始消费,$ 从队列尾部开始消费)
XGROUP CREATE mystream groupA 0

-- 消费消息(> 表示从第一条未被消费过的消息消费)
XREADGROUP GROUP groupA consumer1 COUNT 2 STREAMS mystream >

-- 查看消费者组的消费进度
XPENDING mystream groupA

四、SpringBoot 集成 Redis

✅10. Maven 依赖与配置

Maven 依赖:

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

核心配置(application.yml):

yaml 复制代码
spring:
  data:
    redis:
      host: 192.168.65.214
      port: 6379
      password: 123qweasd
      # ... 更多配置

✅11. RestTemplate 快速上手

记住一个对象:

java 复制代码
@Resource
private RedisTemplate<String, Object> redisTemplate;

按组操作:

java 复制代码
redisTemplate.opsForValue().xxx           // String 类型
redisTemplate.opsForSet().xxx             // Set 类型
redisTemplate.opsForHash().xxx            // Hash 类型
redisTemplate.opsForList().xxx            // List 类型
redisTemplate.opsForZset().xxx            // ZSet 类型
redisTemplate.opsForGeo().xxx             // Geo 类型
redisTemplate.opsForHyperLogLog().xxx     // HyperLogLog 类型
redisTemplate.opsForStream().xxx          // Stream 类型
redisTemplate.opsForValue().setBit()      // Bitmap 类型(基于 String)

📝 注意: Bitmap 没有单独的操作类型(opsForBit),而是通过 opsForValue().setBit() 来操作,因为 Bitmap 本质上是基于 String 的位操作。

✅12. RedisTemplate 解决中文乱码

java 复制代码
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
    RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
    redisTemplate.setConnectionFactory(redisConnectionFactory);

    // GenericJackson2JsonRedisSerializer jsonSerializer = new GenericJackson2JsonRedisSerializer();
    StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
    GenericToStringSerializer<String> genericToStringSerializer = new GenericToStringSerializer<>(String.class);

    // 指定 key 和 value 的序列化方式
    redisTemplate.setKeySerializer(stringRedisSerializer);
    redisTemplate.setValueSerializer(genericToStringSerializer);
    redisTemplate.setHashKeySerializer(stringRedisSerializer);
    redisTemplate.setHashValueSerializer(stringRedisSerializer);

    redisTemplate.afterPropertiesSet();
    return redisTemplate;
}

📋 全文总结

✅1. 数据结构总览对比

数据结构 底层实现 特点 典型场景
String SDS(动态字符串) 基础类型,最大 512MB 缓存、分布式锁、计数器
Hash listpack / hashtable 适合存储对象 对象缓存、购物车
List quicklist(双向链表) 有序、可重复 栈、队列、阻塞队列
Set listpack / hashtable 无序、去重 标签、点赞、抽奖、集合运算
ZSet listpack + skiplist 有序、去重、按分数排序 排行榜
Bitmap String 位操作 极省空间(1bit/位) 签到、活跃统计
HyperLogLog 概率统计算法 12KB 固定内存,0.81% 误差 UV 统计、大基数去重
Geo ZSet + GeoHash 地理位置计算 附近的人/店铺
Stream Rax(基数树) 消息队列 轻量级 MQ

✅2. 面试高频考点

  1. String 实现分布式锁: SET key value EX 10 NX 原子操作,注意锁超时和锁释放的安全性
  2. Hash vs String 存对象: Hash 更节省空间,可以针对单个 field 操作,但 field 不支持独立过期
  3. List 实现的三种结构: Stack = LPUSH+LPOP,Queue = LPUSH+RPOP,Blocking MQ = LPUSH+BRPOP
  4. Set 实现社交关系: SINTER(共同关注)、SUNION(朋友圈)、SDIFF(推荐好友)
  5. ZSet 实现排行榜: ZINCRBY 增加分数,ZREVRANGE 倒序获取排名
  6. HyperLogLog vs Set 做去重: HyperLogLog 省内存但有误差,Set 精确但占内存
  7. Redis Stream vs 专业 MQ: Stream 轻量级、低延迟,但不能持久化、没有 ACK 机制,企业级选 Kafka/RocketMQ

✅3. 数据结构选择决策树

复制代码
需要排序?
├── 是 → 用 ZSet
└── 否 → 需要去重?
    ├── 是 → 统计基数?
    │   ├── 是 → HyperLogLog(大基数估算)
    │   └── 否 → Set / ZSet
    └── 否 → 存对象?
        ├── 是 → Hash(可单字段操作)
        └── 否 → 有队列特性?
            ├── 是 → List / Stream
            └── 否 → String