在使用Redis做缓存时,如果要把一个用户的姓名、年龄、邮箱这些成组的字段存进去,很多方案会把整个对象序列化成JSON字符串再塞进一个String键里。这种做法能用,但每次改一个字段都要把整个对象读出来、反序列化、修改、再序列化写回,开销不小。Redis的哈希类型正是为这类场景设计的,而HSET就是往哈希里写字段的核心命令。掌握它的用法、返回值和边界情况,是用好哈希结构的第一步。

HSET的基本语法与返回值
HSET的完整语法是HSET key field value [field value ...]。它把哈希表key中的字段field设置为value。如果key不存在,Redis会先创建一个空的哈希表再执行写入;如果字段已经存在,则直接覆盖旧值。从Redis 4.0开始,HSET支持一次设置多个字段值对,这一点和被废弃的HMSET基本等价。
HSET的返回值比较有意思,值得单独说清楚:它返回的是本次操作中新创建的字段数量。如果所有字段都是新加的,返回字段个数;如果某个字段原本就存在,本次只是覆盖更新,那么这个字段不计入返回值。看下面的例子:
127.0.0.1:6379> HSET user:1001 name zhangsan age 25 (integer) 2 127.0.0.1:6379> HSET user:1001 age 26 city beijing (integer) 1 127.0.0.1:6379> HGET user:1001 age "26"
第一条命令新建了name和age两个字段,返回2;第二条命令中age是覆盖操作,city是新建,所以返回1。通过返回值可以判断某次写入是插入还是更新,这在做一些统计逻辑时很有用。
HSET、HMSET与HSETNX的区别
很多老教程里会看到HMSET命令,它的作用是批量设置多个字段。但在Redis 4.0之后,HSET本身已经支持多字段写入,HMSET被标记为废弃状态,执行时虽然还能工作,但会返回一个警告提示。新代码中直接用HSET即可,没必要再区分单条和批量。
HSETNX则不同,它的语义是"只在字段不存在时才设置",全称可以理解为HASH SET if Not eXists。它适合做防覆盖写入,比如分布式环境下初始化配置项、防止并发请求重复写初始值。对比一下:
127.0.0.1:6379> HSETNX lock:order field1 value1 (integer) 1 127.0.0.1:6379> HSETNX lock:order field1 value2 (integer) 2 返回值实际为 0,写入被忽略
注意上面第二条命令的真实返回是0,表示没有执行写入,field1的值仍然是value1。HSETNX在需要幂等性保护的场景下比HSET更安全,但要注意它是字段级别的判断,不是键级别的锁,用它做分布式锁并不严谨。
典型应用场景与代码示例
哈希结构最常见的用途是存储对象。以用户资料缓存为例,假设MySQL里有一张user表,读取后写入Redis,用PHP操作可以这样写:
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$key = 'user:' . $userId;
// 批量写入多个字段,等价于 HSET key f1 v1 f2 v2 ...
$redis->hMSet($key, [
'name' => $userInfo['name'],
'age' => $userInfo['age'],
'city' => $userInfo['city'],
]);
// 单独更新一个字段,例如用户修改了昵称
$redis->hSet($key, 'name', $newName);
// 读取时可以只取需要的字段,避免整个对象反序列化
$name = $redis->hGet($key, 'name');
另一个经典场景是购物车。把用户ID作为key的一部分,商品ID作为field,数量作为value,加购、改数量、删商品分别对应HSET、HSET覆盖和HDEL,操作粒度刚好匹配业务需求。相比用JSON字符串存整个购物车,哈希方案改一个商品数量只需一次命令,网络开销和序列化成本都低得多。
使用HSET需要注意的坑
首先是value的类型问题。HSET写入的值都会被当作字符串处理,传入数字也会转成字符串存储。如果用浮点数写入,精度可能发生变化,取出来后再参与计算要显式转型,不要依赖隐式转换。
其次是大key风险。哈希的字段数没有硬性上限,如果把几十万个字段塞进一个key里,HSET虽然仍是O(1)复杂度,但整个key的序列化、过期删除、主从同步都会变慢,还可能触发Redis的阻塞式操作。建议单key字段数控制在几千以内,超过时考虑按某种规则拆分成多个key,比如按字段哈希分片。
最后是过期问题。Redis的过期时间是键级别的,HSET无法对单个字段设置TTL。如果需要字段级过期,常见做法是额外维护一个ZSET记录过期时间戳,定期清理;或者拆分成多个独立的String键。选哪种方案,取决于字段数量和对精度的要求。理解了这些限制,HSET用起来才能既高效又稳。
Redis HSETRedis哈希Redis数据类型修改时间:2026-09-16 14:42:36