引言
在进入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数据结构仍然是不可替代的选择。