【Redis】字符串与哈希Day3(2026年)
2026/5/30 9:40:41 网站建设 项目流程

写在前面

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

文章目录

    • 写在前面
    • 一、String命令详解
      • 1.1 基础SET/GET命令
      • 1.2 带参数的SET命令
      • 1.3 数值操作命令
      • 1.4 字符串操作命令
      • 1.5 批量操作命令
    • 二、String应用场景
      • 2.1 缓存
      • 2.2 计数器
      • 2.3 分布式ID生成
      • 2.4 分布式锁
    • 三、Hash命令详解
      • 3.1 基础操作命令
      • 3.2 字段管理命令
      • 3.3 数值操作命令
      • 3.4 批量迭代命令
    • 四、Hash应用场景
      • 4.1 用户信息存储
      • 4.2 商品信息存储
      • 4.3 购物车实现
    • 五、踩坑提醒:value过大问题
      • 5.1 String类型大value
      • 5.2 Hash类型大value
      • 5.3 如何检测大value
    • 六、命令对比表
      • 6.1 String vs Hash命令对比
      • 6.2 场景选择建议
    • 七、面试高频考点
      • Q1:String和Hash存储对象如何选择?
      • Q2:INCR命令有什么注意事项?
      • Q3:如何实现一个分布式锁?
    • 八、参考资料
    • 九、互动话题

一、String命令详解

1.1 基础SET/GET命令

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

# 基本设置 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命令

# 设置过期时间(秒) 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的数值操作是原子的,非常适合高并发计数场景。

# 设置数值 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 字符串操作命令

# 追加字符串 append name " tutorial" # 获取字符串长度 strlen name # 获取子字符串 getrange name 0 4 # 设置子字符串 setrange name 0 "REDIS"

1.5 批量操作命令

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

注意事项

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

二、String应用场景

2.1 缓存

实际场景:缓存是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 计数器

# 文章阅读量 incr article:1001:views # 用户点赞数 incr user:1001:likes # 获取计数 get article:1001:views # 批量获取多个计数 mget article:1001:views article:1002:views

注意事项

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

2.3 分布式ID生成

# 每天重置的ID set order:id:20240101 0 # 生成订单ID incr order:id:20240101 # 返回1,订单ID为:20240101000001 # 批量获取ID incrby order:id:20240101 100

2.4 分布式锁

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

# 加锁(原子操作) 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更灵活。

# 设置单个字段 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 字段管理命令

# 删除字段 hdel user:1001 city # 检查字段是否存在 hexists user:1001 name # 仅当字段不存在时设置 hsetnx user:1001 email "test@example.com" # 获取字段数量 hlen user:1001

3.3 数值操作命令

# 字段自增整数 hincrby user:1001 age 1 # 字段自增浮点数 hincrbyfloat user:1001 salary 1000.5

3.4 批量迭代命令

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

四、Hash应用场景

4.1 用户信息存储

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

# 存储用户信息 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 商品信息存储

# 存储商品信息 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 购物车实现

# 添加商品到购物车 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
  • 网络传输慢,阻塞其他操作
  • 内存碎片增加

解决方案

# 方案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操作耗时
  • 内存占用高

解决方案

# 拆分为多个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

# 使用redis-cli扫描大keyredis-cli--bigkeys# 查看指定key的内存占用redis-cli memory usage keyname# 使用RDB工具分析rdb--commandjson dump.rdb|python analyze.py

六、命令对比表

6.1 String vs Hash命令对比

操作String命令Hash命令
设置值SET key valueHSET key field value
获取值GET keyHGET key field
删除DEL keyHDEL key field
检查存在EXISTS keyHEXISTS key field
自增INCR keyHINCRBY key field num
批量设置MSET k1 v1 k2 v2HMSET key f1 v1 f2 v2
批量获取MGET k1 k2HMGET key f1 f2

6.2 场景选择建议

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

七、面试高频考点

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

答案

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

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

答案

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

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

答案

# 加锁(原子操作) 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的命令细节与应用场景。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询