【Redis】字符串与哈希Day3(2026年)

写在前面

String和Hash是Redis中最常用的两种数据类型,掌握它们的命令细节和应用场景,能让你在实际开发中游刃有余。今天我们深入探讨这两种类型的核心命令与实战技巧。

文章目录


一、String命令详解

1.1 基础SET/GET命令

实际场景:SET/GET是最基础也是最常用的命令,但很多细节你可能不知道。

redis 复制代码
# 基本设置
set name "redis"

# 基本获取
get name

# 设置并返回旧值
getset name "new_redis"

# 仅当key不存在时设置
setnx name "redis"

# 仅当key存在时设置
set name "redis" xx

上述命令中,GETSET常用于原子性更新并获取旧值的场景;SETNX是分布式锁的基础;XX参数表示仅当key存在时才设置。

1.2 带参数的SET命令

redis 复制代码
# 设置过期时间(秒)
set session:token "user_data" ex 3600

# 设置过期时间(毫秒)
set session:token "user_data" px 3600000

# 设置过期时间戳(秒)
set session:token "user_data" exat 1700000000

# NX参数:仅当key不存在时设置
set lock:order "uuid" nx ex 30

# GET参数:设置新值并返回旧值
set counter 100 get

注意事项

  • EX和PX不能同时使用
  • NX和XX不能同时使用
  • SET key value NX EX seconds 是原子操作,适合实现分布式锁

1.3 数值操作命令

经验之谈:Redis的数值操作是原子的,非常适合高并发计数场景。

redis 复制代码
# 设置数值
set counter 0

# 自增1
incr counter

# 自增指定值
incrby counter 10

# 自增浮点数
incrbyfloat counter 2.5

# 自减1
decr counter

# 自减指定值
decrby counter 5
命令 说明 返回值
INCR 自增1 自增后的值
INCRBY 自增指定整数 自增后的值
INCRBYFLOAT 自增浮点数 自增后的值
DECR 自减1 自减后的值
DECRBY 自减指定整数 自减后的值

注意事项

  • 如果key不存在,INCR会先初始化为0再自增
  • 如果value不是整数,INCR会报错
  • INCRBYFLOAT支持科学计数法

1.4 字符串操作命令

redis 复制代码
# 追加字符串
append name " tutorial"

# 获取字符串长度
strlen name

# 获取子字符串
getrange name 0 4

# 设置子字符串
setrange name 0 "REDIS"

1.5 批量操作命令

redis 复制代码
# 批量设置
mset key1 "value1" key2 "value2" key3 "value3"

# 批量获取
mget key1 key2 key3

# 批量设置(原子操作,任一失败则全部失败)
msetnx key1 "value1" key2 "value2"

注意事项

  • MGET返回的是数组,不存在的key返回nil
  • MSETNX是原子操作,适合需要保证一致性的批量设置

二、String应用场景

2.1 缓存

实际场景:缓存是Redis最常见的用途,能显著提升系统性能。

redis 复制代码
# 设置缓存(带过期时间)
set cache:user:1001 '{"name":"zhangsan","age":25}' ex 3600

# 获取缓存
get cache:user:1001

# 缓存不存在时的处理(伪代码)
# if redis.get(key) is None:
#     data = db.query(...)
#     redis.set(key, data, ex=3600)

缓存策略对比

策略 说明 适用场景
Cache Aside 先查缓存,不存在再查库 读多写少
Write Through 写入时同时更新缓存 读写均衡
Write Behind 先写缓存,异步写库 写多读少

2.2 计数器

redis 复制代码
# 文章阅读量
incr article:1001:views

# 用户点赞数
incr user:1001:likes

# 获取计数
get article:1001:views

# 批量获取多个计数
mget article:1001:views article:1002:views

注意事项

  • 计数器不需要初始化,INCR自动创建
  • 高并发下INCR是原子操作,不会丢失计数

2.3 分布式ID生成

redis 复制代码
# 每天重置的ID
set order:id:20240101 0

# 生成订单ID
incr order:id:20240101
# 返回1,订单ID为:20240101000001

# 批量获取ID
incrby order:id:20240101 100

2.4 分布式锁

踩坑提醒:分布式锁实现要考虑锁超时、误删等问题。

redis 复制代码
# 加锁(原子操作)
set lock:order:1001 "uuid-xxx" nx ex 30

# 业务逻辑执行...

# 释放锁(Lua脚本保证原子性)
# if redis.call("get", KEYS[1]) == ARGV[1] then
#     return redis.call("del", KEYS[1])
# else
#     return 0
# end

分布式锁注意事项

  1. 设置过期时间,防止死锁
  2. value使用唯一标识,防止误删
  3. 释放锁时验证value,使用Lua保证原子性

三、Hash命令详解

3.1 基础操作命令

实际场景:Hash天生适合存储对象,比String+JSON更灵活。

redis 复制代码
# 设置单个字段
hset user:1001 name "zhangsan"

# 设置多个字段
hmset user:1001 name "zhangsan" age 25 city "beijing"

# 获取单个字段
hget user:1001 name

# 获取多个字段
hmget user:1001 name age city

# 获取所有字段和值
hgetall user:1001

# 获取所有字段名
hkeys user:1001

# 获取所有值
hvals user:1001

3.2 字段管理命令

redis 复制代码
# 删除字段
hdel user:1001 city

# 检查字段是否存在
hexists user:1001 name

# 仅当字段不存在时设置
hsetnx user:1001 email "test@example.com"

# 获取字段数量
hlen user:1001

3.3 数值操作命令

redis 复制代码
# 字段自增整数
hincrby user:1001 age 1

# 字段自增浮点数
hincrbyfloat user:1001 salary 1000.5

3.4 批量迭代命令

redis 复制代码
# 迭代获取字段(适合大Hash)
hscan user:1001 0 match "field_*" count 10

四、Hash应用场景

4.1 用户信息存储

经验之谈:用户信息用Hash存储,可以只更新某个字段,比String+JSON更高效。

redis 复制代码
# 存储用户信息
hmset user:1001 name "zhangsan" age 25 city "beijing" email "zhangsan@example.com"

# 只更新年龄
hset user:1001 age 26

# 只获取姓名和邮箱
hmget user:1001 name email

# 检查邮箱是否存在
hexists user:1001 email

Hash vs String存储对象对比

对比项 Hash存储 String+JSON存储
部分读取 支持(HGET) 需要整体读取
部分更新 支持(HSET) 需要整体更新
内存占用 较小(ziplist) 较大
字段查询 支持(HEXISTS) 需要解析JSON
适用场景 字段多、部分访问 字段少、整体访问

4.2 商品信息存储

redis 复制代码
# 存储商品信息
hmset product:2001 name "iPhone 15" price 6999 stock 100 category "phone"

# 库存减1
hincrby product:2001 stock -1

# 获取库存
hget product:2001 stock

# 获取商品基本信息
hmget product:2001 name price stock

4.3 购物车实现

redis 复制代码
# 添加商品到购物车
hset cart:user:1001 product:2001 2
hset cart:user:1001 product:2002 1

# 修改商品数量
hset cart:user:1001 product:2001 3

# 获取购物车所有商品
hgetall cart:user:1001

# 删除购物车商品
hdel cart:user:1001 product:2002

# 获取购物车商品数量
hlen cart:user:1001

五、踩坑提醒:value过大问题

踩坑提醒:大value是Redis性能杀手,务必控制value大小!

5.1 String类型大value

问题表现

  • 单个String value超过10KB
  • 网络传输慢,阻塞其他操作
  • 内存碎片增加

解决方案

redis 复制代码
# 方案1:压缩存储
# 将大对象压缩后存储
set big:object "compressed_data" ex 3600

# 方案2:拆分存储
set user:1001:profile "基本信息"
set user:1001:detail "详细信息"
set user:1001:settings "设置信息"

# 方案3:使用Hash拆分
hmset user:1001 profile "基本信息" detail "详细信息" settings "设置信息"

5.2 Hash类型大value

问题表现

  • 单个Hash字段数超过5000
  • HGETALL操作耗时
  • 内存占用高

解决方案

redis 复制代码
# 拆分为多个Hash
hmset user:1001:basic name "zhangsan" age 25
hmset user:1001:contact email "xxx" phone "xxx"
hmset user:1001:address city "beijing" street "xxx"

# 使用HSCAN迭代获取
hscan user:1001:basic 0

5.3 如何检测大value

shell 复制代码
# 使用redis-cli扫描大key
redis-cli --bigkeys

# 查看指定key的内存占用
redis-cli memory usage keyname

# 使用RDB工具分析
rdb --command json dump.rdb | python analyze.py

六、命令对比表

6.1 String vs Hash命令对比

操作 String命令 Hash命令
设置值 SET key value HSET key field value
获取值 GET key HGET key field
删除 DEL key HDEL key field
检查存在 EXISTS key HEXISTS key field
自增 INCR key HINCRBY key field num
批量设置 MSET k1 v1 k2 v2 HMSET key f1 v1 f2 v2
批量获取 MGET k1 k2 HMGET key f1 f2

6.2 场景选择建议

场景 推荐类型 理由
简单KV缓存 String 简单高效
对象存储(字段多) Hash 支持部分读写
计数器 String INCR原子操作
分布式锁 String SET NX EX原子操作
购物车 Hash field为商品ID,value为数量
用户Session String 整体存取,带过期时间

七、面试高频考点

Q1:String和Hash存储对象如何选择?

答案

  • 字段少(<5个)且整体访问:选String
  • 字段多或需要部分访问:选Hash
  • 需要设置过期时间:选String(Hash不支持对单个field设置过期)
  • 内存敏感:选Hash(ziplist编码更省内存)

Q2:INCR命令有什么注意事项?

答案

  1. value必须是整数格式,否则报错
  2. key不存在时会初始化为0再自增
  3. 范围是64位有符号整数,超出范围会报错
  4. 是原子操作,高并发下安全

Q3:如何实现一个分布式锁?

答案

redis 复制代码
# 加锁(原子操作)
set lock:resource "uuid" nx ex 30

# 释放锁(Lua脚本保证原子性)
eval "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end" 1 lock:resource "uuid"

关键点:

  1. 使用SET NX EX原子加锁
  2. value使用唯一标识防止误删
  3. 释放锁时验证value
  4. 设置合理的过期时间

八、参考资料

Redis内存优化指南


九、互动话题

你在项目中遇到过String和Hash选型的困惑吗?有没有因为选错类型导致的性能问题?欢迎在评论区分享你的经验!

下一篇我们将深入探讨List和Set的命令细节与应用场景。

相关推荐
sakoba1 小时前
MySQL常见问题学习
数据库·学习·mysql
swipe1 小时前
Neo4j + Graph RAG 工程实践:RAG 真正缺的不是更多文本,而是可查询的关系
后端·面试·llm
小二·1 小时前
向量数据库深度对比:PGVector vs Qdrant vs Milvus vs Chroma(附性能测试数据)
数据库·wpf·milvus
sleven fung1 小时前
Milvus 向量数据库
开发语言·数据库·python·langchain·milvus
神奇小汤圆1 小时前
告别OOM焦虑:Flink 内存模型原理与诊断调优
后端
赵渝强老师2 小时前
【赵渝强老师】崖山数据库的数据字典
数据库·oracle
java_cj2 小时前
MySQL 8.0 新特性深度解析:降序索引、Doublewrite Buffer 与 redo log 无锁优化
数据库·mysql
神奇小汤圆2 小时前
Kafka性能调优:从10万到100万条/秒的实战经验
后端
网管NO.12 小时前
多表联查入门|INNER JOIN 内连接,关联查询基础(实操案例)
数据库·sql