Redis值数据类型——hash

4.2 hash

4.2.1 概述

  1. 需求背景
    要在 Redis 中存储包含多属性的用户信息(如生日、姓名、年龄等),对比 String 类型存储方案的问题,引出 Hash 类型的优势。
  2. String 类型存储的痛点
    将用户对象序列化为 JSON 字符串存入 Redis 后,若仅需更新单个属性(如 age),必须先读取整个 JSON 字符串、反序列化为对象、修改属性后再序列化回存,会造成传输和处理的资源浪费。
  3. Redis Hash 类型核心特点
  • 是 "字段 - 字段值" 的映射结构,专门适配多属性对象的存储;
  • 字段值仅支持字符串类型,不支持嵌套 Hash、集合等复杂类型;
  • 可直接操作单个字段(如仅更新 age),无需处理整个对象,解决了 String 类型的资源浪费问题。

4.2.2 命令

  1. 赋值和取值
    为指定 hash 的单个字段赋值,支持插入 / 更新,通过返回值可判断操作类型。
    返回值为1:插入(字段不存在)
    返回值2:更新(字段存在)

    hset key field value

获取指定 hash 的单个字段值

复制代码
hget key field
  1. 批量赋值和取值

    #为指定 hash 批量赋值
    hmset key field value [field value ...]
    #获取指定 hash 的多个字段值
    hmget key field [field ...]

  1. 获取指定 hash 的所有字段和值

    hgetall key

返回 "字段 + 值" 交替的列表

  1. 判断字段是否存在
    查看字段是否存在,存在返回1,不存在返回0。

    hexists key field

查看字段是否存在,当字段不存在时赋值,类似HSET(返回1),区别在于如果字段已经存在,该命令不执行任何操作(返回0)。

复制代码
 hsetnx key field value
  1. 删除字段
    可以删除一个或多个字段,返回值是被删除的字段个数。

    hdel key field [field ...]

删除多个字段时,只删除存在的字段,不存在的直接跳过。

  1. 只获取字段名或字段值

    hkeys key
    hvals key

7.获取字段数量

复制代码
hlen key
  1. 获取key下的所有字段和对应的值
    获取指定 Hash 键(key)下所有的字段和对应的值,返回结果是 "字段 + 值" 交替的列表。
    返回格式是 "字段 1、值 1、字段 2、值 2......" 的顺序,Redis 会保证字段和值的一一对应。

    hgetall key

  1. 字段的增量运算
    给指定 Hash 键(key)下的指定字段(field)的整数值,加上增量值 increment(本质就是做加法运算)。

    hincrby key field increment

注:

  • 仅支持整数运算,字段值必须是整数格式的字符串(如 "100"),小数会报错;

  • 原子性操作:多线程 / 多服务并发操作时,不会出现计算错误(比如并发加 1,结果不会少加);

  • 字段不存在时:自动将字段值初始化为 0,再执行加法(比如给不存在的 count 字段加 10,结果就是 10)。

    1. 给hashs的entryKey字段值加123(字段初始值为0)
      127.0.0.1:6379> hincrby hashs entryKey 123
      (integer) 123
    2. 再次执行,继续加50(123+50=173)
      127.0.0.1:6379> hincrby hashs entryKey 50
      (integer) 173
    3. 减法(增量为负数即可):173-80=93
      127.0.0.1:6379> hincrby hashs entryKey -80
      (integer) 93

4.2.3 方法

  1. Hash 类型核心操作

|----------|-----------------------------------------|-------------|-------------------------|
| 功能 | StringRedisTemplate 方法 | 对应 Redis 命令 | 代码中的作用 |
| 单个设置字段 | opsForHash().put(key, field, value) | HSET | 给指定 hash 的单个字段赋值 |
| 批量设置字段 | opsForHash().putAll(key, Map) | HMSET | 一次性给 hash 设置多个字段 - 值 |
| 单个获取字段值 | opsForHash().get(key, field) | HGET | 获取指定 hash 的单个字段值 |
| 批量获取字段值 | opsForHash().multiGet(key, List) | HMGET | 一次性获取 hash 的多个字段值 |
| 删除指定字段 | opsForHash().delete(key, fields...) | HDEL | 删除 hash 中的一个 / 多个字段 |
| 获取字段数量 | opsForHash().size(key) | HLEN | 返回 hash 中字段的总个数 |
| 判断字段是否存在 | opsForHash().hasKey(key, field) | HEXISTS | 检查 hash 中指定字段是否存在 |
| 获取所有字段名 | opsForHash().keys(key) | HKEYS | 返回 hash 中所有字段名(Set 集合) |
| 获取所有字段值 | opsForHash().values(key) | HVALS | 返回 hash 中所有字段值(List 集合) |
| 获取所有字段和值 | opsForHash().entries(key) | HGETALL | 返回 hash 中所有字段 - 值(Map) |
| 字段值增量更新 | opsForHash().increment(key, field, num) | HINCRBY | 给 hash 字段值做数字增量更新 |

  1. Hash 关联的 Key 通用操作
    同4.1.3
  • 返回值类型:Hash 操作的返回值多为 Object/Set
    *

    • 增量更新限制:increment 仅支持数字类型字段值,需先确保字段值为数字(如代码中先设 entryKey 为 "0"),否则抛异常;
    • 连接管理:所有 Hash 操作底层自动复用连接池,无需手动创建 / 关闭连接。
    1. 结论
    • Hash 类型操作的核心套路:stringRedisTemplate.opsForHash().xxx();
    • 批量操作(putAll/multiGet)适配 Map/List 入参,与 String 类型的 multiSet/multiGet 逻辑一致;
    • Hash 操作区分 "字段级(如 hasKey(key, field))" 和 "键级(如 hasKey(key))",前者查字段、后者查整个 hash 键。
    1. 代码

    package com.qcby.springbootredis;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.data.redis.core.StringRedisTemplate;
    import java.util.*;
    @SpringBootTest
    public class HashTest {
    // 注入SpringBoot默认的StringRedisTemplate(替代Jedis)
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**

    • Hash类型核心操作
      */
      @Test
      public void testHash() {
      System.out.println("==Hash==");
      try {
      // ========== 1. 批量设置Hash字段(对应hmset) ==========
      Map<String, String> kidMap = new HashMap<>();
      kidMap.put("name", "Akshi");
      kidMap.put("age", "2");
      kidMap.put("sex", "Female");
      stringRedisTemplate.opsForHash().putAll("kid", kidMap);

      复制代码
       // ========== 2. 获取单个/多个Hash字段值(对应hmget) ==========
       // 获取单个字段
       List<Object> nameList = stringRedisTemplate.opsForHash().multiGet("kid", Collections.singletonList("name"));
       System.out.println(nameList); // 输出 [Akshi]
      
       // ========== 3. 删除指定Hash字段(对应hdel) ==========
       stringRedisTemplate.opsForHash().delete("kid", "age");
       // 获取已删除的字段(返回null)
       List<Object> pwdList = stringRedisTemplate.opsForHash().multiGet("kid", Collections.singletonList("pwd"));
       System.out.println(pwdList); // 输出 [null]
      
       // ========== 4. Hash通用操作 ==========
       System.out.println(stringRedisTemplate.opsForHash().size("kid")); // 字段数量(hlen),输出 2
       System.out.println(stringRedisTemplate.hasKey("kid")); // 判断key是否存在(exists),输出 true
       System.out.println(stringRedisTemplate.opsForHash().keys("kid")); // 获取所有字段名(hkeys),输出 [name, sex]
       System.out.println(stringRedisTemplate.opsForHash().values("kid")); // 获取所有字段值(hvals),输出 [Akshi, Female]
      
       // 遍历所有字段和值
       Set<Object> kidKeys = stringRedisTemplate.opsForHash().keys("kid");
       for (Object key : kidKeys) {
           String value = (String) stringRedisTemplate.opsForHash().get("kid", key);
           System.out.println(key + ":" + value);
       }
      
       // 批量获取指定字段值
       List<Object> values = stringRedisTemplate.opsForHash().multiGet("kid", Arrays.asList("name", "age", "sex"));
       System.out.println(values); // 输出 [Akshi, null, Female]
      
       // 获取Hash所有字段和值(hgetAll)
       Map<Object, Object> kidAll = stringRedisTemplate.opsForHash().entries("kid");
       System.out.println(kidAll); // 输出 {name=Akshi, sex=Female}
      
       // ========== 5. 清空当前库(对应flushDB) ==========
       stringRedisTemplate.getConnectionFactory().getConnection().flushDb();
       System.out.println("清空数据库成功");
      
       // ========== 6. 单个设置Hash字段(对应hset) ==========
       stringRedisTemplate.opsForHash().put("hashs", "entryKey", "entryValue");
       stringRedisTemplate.opsForHash().put("hashs", "entryKey1", "entryValue1");
       stringRedisTemplate.opsForHash().put("hashs", "entryKey2", "entryValue2");
      
       // ========== 7. 判断Hash字段是否存在(对应hexists) ==========
       System.out.println(stringRedisTemplate.opsForHash().hasKey("hashs", "entryKey")); // 输出 true
      
       // ========== 8. 获取单个Hash字段值(对应hget) ==========
       System.out.println(stringRedisTemplate.opsForHash().get("hashs", "entryKey")); // 输出 entryValue
       // 批量获取字段值
       System.out.println(stringRedisTemplate.opsForHash().multiGet("hashs", Arrays.asList("entryKey", "entryKey1"))); // 输出 [entryValue, entryValue1]
      
       // ========== 9. 删除Hash字段(对应hdel) ==========
       stringRedisTemplate.opsForHash().delete("hashs", "entryKey");
      
       // ========== 10. 字段值增量更新(对应hincrBy) ==========
       // 先给entryKey设值为数字(否则会报错)
       stringRedisTemplate.opsForHash().put("hashs", "entryKey", "0");
       Long incrResult = stringRedisTemplate.opsForHash().increment("hashs", "entryKey", 123L);
       System.out.println(incrResult); // 输出 123
      
       // ========== 11. 再次获取所有字段名和值 ==========
       System.out.println(stringRedisTemplate.opsForHash().keys("hashs")); // 输出 [entryKey, entryKey1, entryKey2]
       System.out.println(stringRedisTemplate.opsForHash().values("hashs")); // 输出 [123, entryValue1, entryValue2]

      } catch (Exception e) {
      e.printStackTrace();
      }
      }

    /**

    • 商品信息存储示例(对应业务场景)
      */
      @Test
      public void testGoodsHash() {
      // 1. 存储商品信息(对应HMSET items:1001 id 3 name apple price 999.9)
      Map<String, String> goodsMap = new HashMap<>();
      goodsMap.put("id", "3");
      goodsMap.put("name", "apple");
      goodsMap.put("price", "999.9");
      stringRedisTemplate.opsForHash().putAll("items:1001", goodsMap);

      // 2. 获取单个字段值(对应HGET items:1001 id)
      String goodsId = (String) stringRedisTemplate.opsForHash().get("items:1001", "id");
      System.out.println("商品ID:" + goodsId); // 输出 3

      // 3. 获取商品所有信息(对应HGETALL items:1001)
      Map<Object, Object> goodsAll = stringRedisTemplate.opsForHash().entries("items:1001");
      System.out.println("商品完整信息:" + goodsAll); // 输出 {id=3, name=apple, price=999.9}
      }

    }

相关推荐
喵喵蒻葉睦2 小时前
力扣 hot100 滑动窗口最大值 单调双端队列 java 简单题解
java·数据结构·算法·leetcode·双端队列·滑动窗口·队列
样例过了就是过了2 小时前
LeetCode热题100 搜索二维矩阵
数据结构·c++·算法·leetcode·矩阵
2401_831920742 小时前
C++与Qt图形开发
开发语言·c++·算法
Shining05962 小时前
AI 编译器系列(四)《AI 编译器中的后端优化》
linux·服务器·人工智能·线性代数·算法·triton·ai编译器
像污秽一样2 小时前
算法设计与分析-习题8.1
数据结构·算法·dfs·dp
飞天狗1112 小时前
最小生成树算法
算法
H_老邪2 小时前
贪心算法的应用
算法·ios·贪心算法
葳_人生_蕤2 小时前
Hot100——739.每日温度
数据结构·算法
Elsa️7462 小时前
洛谷p1046:用一个题练习排序+二分查找
c++·算法