48、Redis哈希操作:HSET/HGET、HMSET/HMGET、HGETALL、HDEL、HINCRBY
各位好,今天我们来聊聊Redis里最常用的数据结构之一——哈希(Hash)。
说实话,我在做嵌入式项目时,哈希用得比字符串还多。为什么呢?因为它太适合存「对象」了。你想想看,一个用户信息、一个设备状态、一个配置项……用哈希存,既清晰又高效。
哈希是什么?
Redis的哈希,说白了就是一个键值对集合。它像C语言里的结构体,也像一张小型的映射表。
# 一个哈希的结构
user:1001 -> {
"name": "张三",
"age": "28",
"city": "北京"
}
每个哈希可以存最多2^32-1个字段,够你用了吧?
HSET 和 HGET:最基础的操作
这两个命令是哈希操作的基石。HSET用来设置字段值,HGET用来读取。
# 设置一个字段
HSET user:1001 name "张三"
# 读取这个字段
HGET user:1001 name
# 返回: "张三"
我在项目中遇到过一个问题:用HSET时,如果字段已经存在,它会覆盖旧值并返回0。这个返回值其实挺有用的——你可以用它判断是「新增」还是「更新」。
HMSET 和 HMGET:批量操作省时间
一次设一个字段太慢了。尤其是你要存一个对象时,字段可能有十几个。这时候就该HMSET上场了。
# 批量设置
HMSET user:1001 name "张三" age "28" city "北京" phone "138xxxx"
# 批量读取
HMGET user:1001 name age city
# 返回: "张三" "28" "北京"
我个人习惯用HMSET来初始化一个对象。一次网络往返,搞定所有字段。效率比循环HSET高太多了。
HGETALL:一把抓出所有数据
有时候你需要整个哈希的所有字段和值。HGETALL就是干这个的。
# 获取所有字段和值
HGETALL user:1001
# 返回: "name" "张三" "age" "28" "city" "北京" "phone" "138xxxx"
注意,它返回的是交替排列的字段名和字段值。在C语言里解析时,记得按「键-值-键-值」的顺序读。
我曾经在调试一个设备状态监控程序时,发现HGETALL返回的数据量太大,导致网络缓冲区溢出。嗯,这里要注意:如果哈希很大(比如上千个字段),HGETALL会一次性返回所有数据,小心内存和带宽。
HDEL:删除字段
哈希里的字段可以单独删除,不用删整个键。
# 删除一个字段
HDEL user:1001 phone
# 删除多个字段
HDEL user:1001 age city
HDEL返回被删除字段的数量。如果字段不存在,它不会报错,只是返回0。
HINCRBY:原子自增
这个命令我特别喜欢。它让哈希里的数字字段可以原子性地增加。
# 初始值
HSET user:1001 score 100
# 增加50
HINCRBY user:1001 score 50
# 返回: 150
# 减少30(用负数)
HINCRBY user:1001 score -30
# 返回: 120
为什么说它重要?因为它是原子操作。在多线程或多客户端环境下,你不用加锁就能安全地更新计数器。
我记得有一次做在线用户统计,用HINCRBY来记录每个用户的登录次数。并发量很大,但从来没出过数据不一致的问题。原子操作就是省心。
知识体系图
下面这张图帮你理清Redis哈希操作的整体脉络:
实际应用场景
哈希在嵌入式系统里特别实用。我给你列几个我亲手做过的例子:
- 设备配置存储:每个设备一个哈希,字段就是配置项名。用HGETALL读取全部配置,用HSET修改单个参数。
- 传感器数据缓存:用HINCRBY累加传感器计数,用HGET读取当前值。
- 用户会话管理:每个会话一个哈希,存登录时间、IP、权限等。用HDEL清理过期会话。
避坑指南
我踩过的坑,分享给你:
- 哈希太大:一个哈希别超过几百个字段。否则HGETALL会变慢,内存占用也高。我建议拆成多个哈希,按业务逻辑分组。
- 字段值类型:Redis哈希的字段值都是字符串。存数字时,HINCRBY会自动转成整数运算。但如果你用HSET存了一个非数字字符串,再用HINCRBY就会报错。
- 内存开销:每个哈希有额外元数据。小哈希(几十个字段)用ziplist编码,省内存。大哈希会自动转成hashtable,性能更好但更占内存。
好了,哈希操作就聊到这里。代码写起来其实很简单,关键是理解每个命令的适用场景。下次你存用户信息或设备状态时,试试哈希,你会发现比字符串拼接舒服多了。