引言
Redis 作为高性能的键值对数据库,其核心优势之一是支持丰富的数据类型。5种基本数据类型(String、Hash、List、Set、Sorted Set)覆盖了绝大多数业务场景,掌握它们的特性、命令和用法是使用Redis的基础。本文将从底层特性、核心命令、使用场景、注意事项四个维度,对每种类型进行全面解析。
1. String(字符串)
String 是 Redis 最基础、最常用的数据类型,底层以简单动态字符串(SDS)实现,可存储字符串、数字(整数/浮点数)、二进制数据(如图片、序列化对象),最大存储容量为 512MB。
1.1 核心特性
- 二进制安全:可存储任意二进制数据,如图片、视频流、序列化的对象。
- 兼容字符串与数字:对数字类型可直接执行自增、自减等算术操作。
- 支持过期时间:可设置键的过期时间,自动删除过期数据。
1.2 常用核心命令
| 命令格式 | 功能描述 | 示例 |
|---|---|---|
| SET key value [EX seconds] [PX milliseconds] | 设置键值,可选过期时间(EX秒/PX毫秒) | SET name "zhangsan" EX 3600(1小时过期) |
| GET key | 获取键对应的值 | GET name → "zhangsan" |
| SETNX key value | 键不存在时才设置(原子操作,用于锁场景) | SETNX lock 1 → 1(设置成功),再次执行→0(失败) |
| SETEX key seconds value | 原子性设置键值+过期时间(避免SET+EX的非原子风险) | SETEX code 60 "123456"(60秒过期的验证码) |
| MSET key1 value1 key2 value2... | 批量设置多个键值 | MSET age 20 gender "male" |
| MGET key1 key2... | 批量获取多个键值 | MGET name age → ["zhangsan", "20"] |
| INCR key | 键值为整数时自增1(原子操作) | INCR count → 1(初始不存在时默认从0开始) |
| DECR key | 键值为整数时自减1(原子操作) | DECR count → 0 |
| INCRBY key step | 整数键值自增指定步长 | INCRBY count 5 → 5 |
| DECRBY key step | 整数键值自减指定步长 | DECRBY count 2 → 3 |
| APPEND key str | 字符串末尾追加内容 | APPEND name "123" → "zhangsan123" |
| STRLEN key | 获取字符串长度 | STRLEN name → 11 |
| GETRANGE key start end | 截取字符串(索引从0开始,end=-1表示末尾) | GETRANGE name 0 5 → "zhang" |
1.3 典型使用场景
- 存储单个值:如用户昵称、头像URL、配置参数。
- 计数器:文章阅读量、接口访问次数、点赞数(依赖INCR的原子性)。
- 验证码:存储短信验证码、邮箱验证码,结合SETEX设置过期时间。
- 分布式锁:基于SETNX实现简单分布式锁(配合EX避免死锁)。
- 存储二进制数据:如小图片、序列化的Java对象(需注意序列化方式)。
1.4 注意事项
- 避免存储过大的字符串(如超过100MB),会影响Redis的读写性能和内存碎片。
- 数字类型本质是字符串存储,执行INCR/DECR前需确保值为纯数字,否则会报错。
- SET+EX是非原子操作,高并发场景下建议用SETEX或SET key value EX seconds NX(原子性设置+过期+不存在才设置)。
2. Hash(哈希)
Hash 是一种键值对集合(类似Java的HashMap、Python的dict),底层采用哈希表结构,适合存储结构化数据。每个Hash可以包含多个字段(field)和值(value),字段和值均为字符串类型。
2.1 核心特性
- 结构化存储:无需序列化,可直接操作单个字段,比String存储结构化数据更高效。
- 空间高效:Hash类型在字段较少时会采用压缩存储(ziplist),节省内存。
- 原子操作:支持对单个字段的原子性读写,适合并发场景。
2.2 常用核心命令
| 命令格式 | 功能描述 | 示例 |
|---|---|---|
| HSET key field value | 设置Hash的字段值(覆盖已存在字段) | HSET user:100 id 100 name "zhangsan" |
| HSETNX key field value | 字段不存在时才设置(原子操作) | HSETNX user:100 age 20 → 1(成功) |
| HGET key field | 获取Hash中指定字段的值 | HGET user:100 name → "zhangsan" |
| HMSET key field1 value1 field2 value2... | 批量设置Hash的多个字段 | HMSET user:100 gender "male" city "beijing" |
| HMGET key field1 field2... | 批量获取Hash的多个字段 | HMGET user:100 name age → ["zhangsan", "20"] |
| HGETALL key | 获取Hash中所有字段和值(字段多时有性能风险) | HGETALL user:100 → [id,100,name,zhangsan,...] |
| HKEYS key | 获取Hash中所有字段名 | HKEYS user:100 → [id,name,age,gender,city] |
| HVALS key | 获取Hash中所有字段值 | HVALS user:100 → [100,zhangsan,20,male,beijing] |
| HLEN key | 获取Hash中字段的数量 | HLEN user:100 → 5 |
| HEXISTS key field | 判断Hash中字段是否存在 | HEXISTS user:100 age → 1(存在) |
| HDEL key field1 field2... | 删除Hash中指定字段 | HDEL user:100 city → 1(删除成功) |
| HINCRBY key field step | 字段值为整数时自增指定步长 | HINCRBY user:100 score 10 → 10 |
| HSTRLEN key field | 获取Hash字段值的字符串长度 | HSTRLEN user:100 name → 8 |
2.3 典型使用场景
- 存储用户信息:如用户ID为key,字段包含name、age、gender、phone等。
- 存储商品信息:商品ID为key,字段包含price、stock、sales、category等。
- 配置项存储:如系统配置、服务参数,按模块划分Hash(如config:redis、config:mysql)。
- 购物车:用户ID为key,商品ID为field,商品数量为value(HINCRBY调整数量)。
2.4 注意事项
- 避免HGETALL操作超大Hash(如字段数超过1000),会阻塞Redis线程,建议用HSCAN分批遍历。
- Hash的字段和值均为字符串,无法直接存储复杂类型(如嵌套结构),需手动序列化。
- 当Hash字段数过多(超过hash-max-ziplist-entries配置,默认512),会自动转为哈希表存储,内存占用增加。
3. List(列表)
List 是有序的字符串列表(类似Java的LinkedList),底层采用双向链表+压缩列表(ziplist)实现,支持从两端插入、删除元素,按索引访问,元素可重复。
3.1 核心特性
- 有序性:元素按插入顺序排列,支持按索引(正/负)访问(负索引从末尾开始,-1为最后一个元素)。
- 双向操作:两端插入(LPUSH/RPUSH)、两端删除(LPOP/RPOP)的时间复杂度均为O(1)。
- 灵活的访问方式:支持获取指定范围元素、按值查找元素、删除指定元素。
3.2 常用核心命令
| 命令格式 | 功能描述 | 示例 |
|---|---|---|
| LPUSH key value1 value2... | 从列表左侧(头部)插入元素 | LPUSH list1 a b c → 列表:[c,b,a] |
| RPUSH key value1 value2... | 从列表右侧(尾部)插入元素 | RPUSH list1 d e → 列表:[c,b,a,d,e] |
| LPOP key | 从列表左侧删除并返回第一个元素 | LPOP list1 → "c",列表变为[b,a,d,e] |
| RPOP key | 从列表右侧删除并返回最后一个元素 | RPOP list1 → "e",列表变为[b,a,d] |
| LRANGE key start end | 获取列表中指定范围的元素(start=0,end=-1获取全部) | LRANGE list1 0 2 → [b,a,d] |
| LLEN key | 获取列表长度 | LLEN list1 → 3 |
| LINDEX key index | 获取列表中指定索引的元素 | LINDEX list1 1 → "a"(索引从0开始) |
| LSET key index value | 修改列表中指定索引的元素值 | LSET list1 1 "x" → 列表变为[b,x,d] |
| LREM key count value | 删除列表中指定值的元素(count>0从左删count个;count<0从右删;count=0删所有) | LREM list1 1 "b" → 删除1个"b",列表变为[x,d] |
| LTRIM key start end | 截取列表,保留指定范围元素,删除其他元素 | LTRIM list1 0 0 → 列表仅保留[x] |
| BRPOP key timeout | 阻塞式从列表右侧删除元素(超时时间timeout秒,0表示永久阻塞) | BRPOP list2 0 → 若list2为空,阻塞等待直到有元素 |
| RPOPLPUSH source destination | 从source列表右侧删除元素,插入到destination列表左侧(原子操作) | RPOPLPUSH list1 list2 → 把list1的"d"移到list2左侧 |
3.3 典型使用场景
- 消息队列:基于LPUSH+BRPOP实现简单生产者-消费者模型(生产者LPUSH入队,消费者BRPOP阻塞出队)。
- 最新消息列表:如朋友圈动态、新闻feed,LPUSH添加新消息,LRANGE获取最新N条。
- 排行榜(简单版):如用户操作日志、商品浏览记录,按时间顺序存储,LRANGE获取历史记录。
- 栈/队列实现:栈(LPUSH+LPOP)、队列(LPUSH+RPOP)、双端队列(LPUSH/RPUSH+LPOP/RPOP)。
- 限流场景:如接口访问频率限制,List存储访问时间戳,LRANGE判断时间窗口内访问次数。
3.4 注意事项
- 列表查询中间元素(如LINDEX)的时间复杂度为O(n),不适合频繁按索引访问大量元素的场景。
- 避免列表过长(如长度超过10000),会导致LRANGE、LTRIM等操作耗时增加,建议拆分列表或用其他类型替代。
- BRPOP是阻塞命令,若多个客户端同时监听同一个列表,会采用公平竞争机制分配元素。
4. Set(集合)
Set 是无序的字符串集合,底层采用哈希表+整数集合(intset)实现,元素不可重复,支持交集、并集、差集等集合运算。
4.1 核心特性
- 唯一性:自动去重,插入重复元素会被忽略。
- 无序性:元素存储无固定顺序,无法按索引访问,只能按值查找。
- 高效的集合运算:交集(SINTER)、并集(SUNION)、差集(SDIFF)的时间复杂度为O(n),但Redis优化后性能优异。
- 随机操作:支持随机获取集合中的元素(SRANDMEMBER)、随机删除元素(SPOP)。
4.2 常用核心命令
| 命令格式 | 功能描述 | 示例 |
|---|---|---|
| SADD key member1 member2... | 向集合中添加元素(重复元素自动忽略) | SADD set1 a b c d → 集合:{a,b,c,d} |
| SREM key member1 member2... | 从集合中删除指定元素 | SREM set1 d → 集合变为{a,b,c} |
| SMEMBERS key | 获取集合中所有元素(无序) | SMEMBERS set1 → [a,c,b](顺序随机) |
| SISMEMBER key member | 判断元素是否在集合中 | SISMEMBER set1 a → 1(存在),SISMEMBER set1 e → 0 |
| SCARD key | 获取集合的元素个数 | SCARD set1 → 3 |
| SINTER key1 key2... | 求多个集合的交集(共同元素) | SADD set2 b c e,SINTER set1 set2 → [b,c] |
| SUNION key1 key2... | 求多个集合的并集(所有元素,去重) | SUNION set1 set2 → [a,b,c,e] |
| SDIFF key1 key2... | 求集合的差集(key1中有但其他集合没有的元素) | SDIFF set1 set2 → [a] |
| SINTERSTORE dest key1 key2... | 求交集并存储到dest集合 | SINTERSTORE set3 set1 set2 → set3={b,c} |
| SRANDMEMBER key [count] | 随机获取集合中的count个元素(默认1个,不删除) | SRANDMEMBER set1 2 → [c,a](随机) |
| SPOP key [count] | 随机删除并返回集合中的count个元素 | SPOP set1 1 → "b",集合变为{a,c} |
| SMOVE source dest member | 把source集合中的member移动到dest集合(原子操作) | SMOVE set1 set2 a → set1={c},set2={b,c,e,a} |
4.3 典型使用场景
- 去重存储:如用户标签(一个用户多个标签,标签不重复)、文章关键词。
- 好友关系:用户A的好友集合为set:A,用户B的好友集合为set:B,SINTER求共同好友,SUNION求所有好友。
- 抽奖活动:存储参与用户ID,SPOP随机抽取中奖用户(避免重复中奖)。
- 黑名单/白名单:如IP黑名单、用户白名单,SISMEMBER快速判断是否在名单中。
- 集合运算场景:如商品分类筛选(交集筛选同时满足多个分类的商品)、用户画像匹配。
4.4 注意事项
- SMEMBERS操作超大集合(元素数超过1000)会阻塞Redis,建议用SSCAN分批遍历。
- 集合运算(SINTER/SUNION/SDIFF)的性能依赖集合大小,避免对超大集合执行此类操作,可先用SCARD判断大小。
- 集合元素为字符串,无法直接存储数字类型,需手动转换为字符串。
5. Sorted Set(有序集合)
Sorted Set(简称ZSet)是有序的字符串集合,底层采用跳表(skiplist)+压缩列表实现,元素不可重复,每个元素关联一个浮点型分数(score),按分数排序。
5.1 核心特性
- 有序性:按分数(score)升序/降序排列,支持按分数范围、排名范围查询。
- 唯一性:元素不可重复,但多个元素可拥有相同分数。
- 高效的排序与查询:跳表结构使插入、删除、查询的时间复杂度均为O(log n),适合大数据量排序场景。
- 灵活的分数操作:支持修改元素分数、按分数增减、按分数筛选元素。
5.2 常用核心命令
| 命令格式 | 功能描述 | 示例 |
|---|---|---|
| ZADD key score1 member1 score2 member2... | 向有序集合添加元素(score为分数,重复member覆盖分数) | ZADD zset1 85 "zhangsan" 90 "lisi" 78 "wangwu" |
| ZREM key member1 member2... | 从有序集合中删除指定元素 | ZREM zset1 "wangwu" → 有序集合变为{zhangsan(85), lisi(90)} |
| ZSCORE key member | 获取指定元素的分数 | ZSCORE zset1 "lisi" → 90.0 |
| ZINCRBY key increment member | 增加指定元素的分数(increment可正负) | ZINCRBY zset1 5 "zhangsan" → 90.0(分数变为90) |
| ZRANGE key start end [WITHSCORES] | 按分数升序排列,获取排名范围内的元素(start=0,end=-1获取全部,WITHSCORES显示分数) | ZRANGE zset1 0 -1 WITHSCORES → [(zhangsan,90), (lisi,90)] |
| ZREVRANGE key start end [WITHSCORES] | 按分数降序排列,获取排名范围内的元素 | ZREVRANGE zset1 0 -1 → [lisi, zhangsan] |
| ZCARD key | 获取有序集合的元素个数 | ZCARD zset1 → 2 |
| ZCOUNT key min max | 统计分数在[min,max]范围内的元素个数 | ZCOUNT zset1 80 90 → 2 |
| ZRANK key member | 获取元素按分数升序的排名(排名从0开始) | ZRANK zset1 "zhangsan" → 0 |
| ZREVRANK key member | 获取元素按分数降序的排名 | ZREVRANK zset1 "zhangsan" → 1 |
| ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count] | 按分数范围筛选元素,支持分页 | ZRANGEBYSCORE zset1 85 95 LIMIT 0 1 → [zhangsan] |
| ZREMRANGEBYRANK key start end | 按排名范围删除元素 | ZREMRANGEBYRANK zset1 1 1 → 删除排名1的元素(zhangsan) |
| ZREMRANGEBYSCORE key min max | 按分数范围删除元素 | ZREMRANGEBYSCORE zset1 80 89 → 删除分数80-89的元素 |
5.3 典型使用场景
- 排行榜:如游戏积分排行榜、商品销量排行榜、文章点赞排行榜(ZADD添加分数,ZREVRANGE获取Top N)。
- 带权重的消息队列:分数为消息优先级,ZRANGEBYSCORE按优先级获取消息。
- 范围查询:如按用户等级(分数)筛选用户、按时间戳(分数)查询一段时间内的记录。
- 延迟队列:分数为过期时间戳,ZRANGEBYSCORE获取已过期的元素,处理后删除。
- 有序去重存储:如按热度排序的商品列表,自动去重且保持有序。
5.4 注意事项
- 分数为浮点型,存在精度问题(如0.1+0.2≠0.3),建议将分数放大为整数存储(如0.1→100)。
- 避免对超大ZSet(元素数超过10万)执行ZRANGE/ZREVRANGE获取全部元素,会阻塞Redis,建议分页查询。
- ZSet的跳表结构占用内存较多,若追求内存效率,可在满足需求的前提下选择其他类型。
总结
Redis 5种基本数据类型各有侧重,适配不同业务场景:
- String:简单值存储、计数器、验证码,通用性最强。
- Hash:结构化数据存储(用户/商品信息),操作单个字段高效。
- List:有序列表、消息队列、最新消息,支持双向操作。
- Set:去重、集合运算、好友关系、抽奖,强调唯一性。
- Sorted Set:排行榜、范围查询、带权重场景,核心是有序性+分数关联。
选择数据类型的核心原则:根据业务需求的“有序性、唯一性、结构化、操作方式”匹配对应类型,同时兼顾Redis的性能和内存占用。
回复