• 如何使用Redis中的Hash数据结构缓存消息
  • 发布于 1周前
  • 33 热度
    0 评论
引言
在进入App页面时,有时需要弹出一些消息以引导用户使用某些功能。最初,每种消息都对应一个独立的接口调用,这虽然方便了业务处理,但也导致了频繁的访问和接口滥用。为了降低接口调用的频次,决定让前端只调用一个接口,并通过多个消息体来传递不同的消息。

消息体格式如下:
{
    "tag": "filterRemind",
    "body": "{\"content\":\"你的滤网马上到期,请及时更换!\"}"
}
在消息体中,tag用于区分不同的业务,body用于展示具体的信息。为了方便对不同的消息进行初始化和赋值,需在程序中定义了一个类来表示消息体:
public class Bubble{
    private String tag;
    private String body;
}
list和hash数据结构的对比
List实践
在业务上,针对某个用户或设备,可能存在多个提醒消息。最初,采用了Redis中的List来存储消息对象:
public class RedisUtil{
    private RedisTemplate<String, Object> redisTemplate;
    // 堆代 duidaima.com
    // 获取list中的全部数据
    public List<Object> lrange(String key, Long start, Long end) {
        return this.redisTemplate.opsForList().range(key, start, end);
    }
    // 删除某个元素
    public Long lrem(String key, Object value, Long count) {
        return this.redisTemplate.opsForList().remove(key, count, value);
    }
    // 放入元素
    public Long lpush(String key, Object value) {
        return this.redisTemplate.opsForList().leftPush(key, value);
    }
}
示例
针对单独设备建立key
String redisKey = RedisUtil.buildRedisKey(RedisKeyConstant.BUBBLE, sn);
// 获取全部数据
List<Bubble> list = redisUtil.lrange(redisKey, 0L, -1L);

// 删除数据
/*
1. 获取全部数据
2. 遍历全部数据,找到要删除的数据
3. 执行删除
*/
for(Bubble b : list){
    if(b.tag.equals(targetTag)){
        redisUtil.lrem(redisKey, b, 1L); // 1L 标识只删除一个
        break;
    }
}
查询某个数据和删除数据一致
使用List作为数据结构时,查询操作还算简单,直接遍历即可。但删除某个元素时就比较耗时,因为需要遍历整个List进行对比,找到目标元素后再进行删除,时间复杂度为O(n)。

Hash实践
在代码评审后,有同事建议使用HSET数据结构来处理,这样会更方便。HSET 是 Redis 中的一个命令,用于在哈希(hash)数据结构中设置字段和值。哈希是一种键值对集合,类似于Java 中的 Map。HSET 命令可以在哈希中添加新的字段或更新已有字段的值。众所周知,Map在插入、删除、更新操作上的时间复杂度都是O(1),因此效率更高。
public class RedisUtil{
    private RedisTemplate<String, Object> redisTemplate;
 // 遍历
    public Map<String, Object> hgetAll(String key) {
        return this.redisTemplate.opsForHash().entries(key);
    }
 // 获取单个数据
    public Object hget(String key, String field) {
        return this.redisTemplate.opsForHash().get(key, field);
    }
 // 删除单个数据
    public void hdel(String key, String field) {
        this.redisTemplate.opsForHash().delete(key, new Object[]{field});
    }
 // 设置数据
    public void hset(String key, String field, Object value) {
        this.redisTemplate.opsForHash().put(key, field, value);
    }
}
示例
//针对单独设备建立key
String redisKey = RedisUtil.buildRedisKey(RedisKeyConstant.BUBBLE, sn);
// 获取全部数据
Map<String, Object> map = redisUtil.hgetAll(redisKey);
for (Object obj : map.values()) {
    if (obj instanceof Bubble) {
        // 强制转型为 Bubble
        Bubble bubble = (Bubble) obj;
    }
}

// 删除数据
redisUtil.hdel(redisKey, targetTag);

// 查询数据
Bubble bubble = (Bubble) redisUtil.hget(redisKey, targetTag);

// 设置数据
Bubble newBubble = new Bubble("filter", "{\"content\":\"你的滤网马上到期,请及时更换!\"}");
redisUtil.hset(redisKey, targetTag, newBubble);
小结
在该业务背景下,使用Hash数据结构明显比List数据结构提升了效率。Hash在插入、删除、更新操作上的时间复杂度为O(1),而List在删除和更新操作上的时间复杂度为O(n)。当然,在需要保持顺序的情况下,List数据结构仍然是不可替代的选择。
用户评论