Redis核心数据结构之Hash(二)

347 阅读1分钟

一、特性说明

基本特性说明

  • 每个hash可以存储2^32-1个f-v
  • hash的field是key的子元素,field不存在,key也不存在。
  • hash的映射关系适合用于存储对象
  • hash结构是可以压缩的,相比string类型来说,会比较节省内存。
    某key的field数量>hash-max-zipmap-entries 采用正常内存存储
    某key的field的某value > hash-max-zipmap-value 采用正常内存存储
    某key的field数量 < hash-max-zipmap-entries 采用zipmap压缩存储
    某key的field的某value < hash-max-zipmap-value 采用zipmap压缩存储

优点和缺点

优点:

  • 同类数据归类整合存储,方便数据管理
  • 相比string操作消耗的内存和cpu更小
  • 相比string存储更节省空间

缺点:

  • 过期功能不能使用在field上,只能用在key上
  • redis集群架构下不适合大规模使用

二、常用命令

(一)HSET

命令格式: hset key field value

功能:一个哈希表key中存入一个键值对

demo说明:
image.png

(二)HGET

命令格式: hget key field

功能:一个哈希表key中取出一个键值对

demo说明:

image.png

(三)HSETNX

命令格式: hsetnx key field value

功能:存入一个不存在的哈希表key的键值

demo说明:

image.png

(四)HMSET

命令格式: hset key field value [field value]

功能:在一个哈希表key中存入多个键值对

demo说明:

image.png

(五)HMGET

命令格式: hget key field [field]

功能:在一个哈希表key中取出多个键值对

demo说明:
image.png

(六)HDEL

命令格式: hdel key field [field]

功能:删除哈希表中key对应的field键值

demo说明: image.png

(七)HLEN

命令格式: hlen key

功能:返回哈希表中key对应的filed个数

demo说明:

image.png

(八)HGETALL

命令格式: hset key field value

功能:返回哈希表中key所有的键值

demo说明:

image.png

三、应用场景

(一)对象数据缓存

说明:系统中对象有多属性并且业务常用获取值判断的场景

1、需求

将用户信息缓存,减少查询数据库

2、设计

  • 登录查询到用户信息,缓存进redis
  • 登出将用户信息从缓存销毁
  • 缓存键设计为 {user}:{用户id}

3、开发

登录存储用户信息

@PostMapping("/test/login")
public void testLogin() throws Exception {

    String key="user:1000";
    //从数据库查询到用户信息
    Users users = new Users();
    users.setMobile("13800138000");
    users.setNickname("张三");

    //这里大家可以在users的内部实现一个tomap方法  我这里为了简单就随便写
    HashMap<String, Object> map = new HashMap<>(16);
    map.put("mobile",users.getMobile());
    map.put("nickName",users.getNickname());

    //批量存入,相当于redis的命令  hmset
    redisTemplate.opsForHash().putAll(key,map);
}

业务需要获取用户信息

@PostMapping("/test/getUserInfo")
public void getUserInfo() throws Exception {

    String key="user:1000";
    
    //键值user里面有多个属性,可以根据自己的需要获取
    List<String> list = new ArrayList<>();
    list.add("mobile");
    list.add("nickName");

    List result = redisTemplate.opsForHash().multiGet(key, list);
    System.out.println(result);
}

退出登录时候将缓存信息删除

@PostMapping("/test/logout")
public void testlogout() throws Exception {

    String key="user:1000";
    //直接把键值删掉即可
    redisTemplate.delete(key);
}

(二)实现演唱会抢票功能

说明:这里主要实现抢票这个环境,不包括前后各种时间判断以及数据落库等等

1、需求

实现抢票功能

2、设计

  • 首先创建场次票务时把座位信息加入缓存
  • 用户锁定座位时判断是否缓存设置成功,成功表示成功锁定
  • 座位集合缓存键设计为 {业务}:{表名}:{场次id}
  • 用户锁定缓存键设计为 user:{业务}:{表名}:{场次id}

3、开发

初始化数据,抢票开始前会将场次的座位号放进缓存的一个集合中,这里用到数据结构set,后面的篇章会讲到

@PostMapping("/test/activeBefore")
public void activeBefore() throws Exception {

    //键值  业务:表名:场次id
    String setkey="purchase:ticket:1000";
    //从数据库获取座位号,这里随便设几个意思意思
    List<String> data = Arrays.asList("A-01","A-02","A-03","A-04","A-05");
    String[] seat =new String[data.size()];
    for (int i = 0; i < data.size(); i++) {
        seat[i] = data.get(i);
    }
    //存入缓存
    redisTemplate.opsForSet().add(setkey,seat);
}

用户抢购电影票

@PostMapping("/test/buyTicket")
public void buyTicket(Integer id,String seatNum) throws Exception {
    
    //键值  业务:表名:场次id  这里的键值设置比较简单,具体要自身业务
    String setkey="purchase:ticket:"+id.toString();

    String buykey="user:purchase:ticket:"+id.toString();

    //判断传进来的座位号是否正确,判断集合中是否存在
    Boolean isMember = redisTemplate.opsForSet().isMember(setkey, seatNum);
    if(!isMember){
        System.out.println("座位信息有误");
        return ;
    }
    
    //抢票,若返回成功,则表示成功锁定席位
    Boolean result = redisTemplate.opsForHash().putIfAbsent(buykey,seatNum, "1");
    if(result){
        System.out.println("锁定席位成功");
    }else{
        System.out.println("该座位已有人购买");
    }

}

(三)电商用户购物车

后续有需要补上