利用redis实现获取top N动态变化的数据
- 服务端收集客户端上报的数据同步写到mysql的一张表中。
- 存在就按客户端唯一标识更新数据,不存在就插入一条。
- 指定一个字段column A用来排序。
- 开启一个定时任务,定时刷新column A 为 top N的数据到一个列表List。
- 每次刷新列表List之前都要和旧的列表做对比,找出差异。
此方案简单,但是缺点有以下几点:
- 高并发情况下直接写mysql导致磁盘IO过高,数据库会是瓶颈。
- 每次都要判断是否存在。
- 维护列表B过大导致JVM内存占用。
- 为了对比前后列表差值,内存开启频繁,gc频繁。 上面找出C并且告知客户端C停止上报,找出新增的D并告知客户端D重新上报数据。 两个List找出差异这并不难,用JAVA里面HashSet的remove api 或者jdk 8的stream实现。
方案2:redis
- 上报数据直接写到zset,客户端唯一标识为member, score为上报的数据(如客户端等级)实时动态更新列表顺序。
- 定时取出zset score为top N的客户端,刷新到本地列表LIST。
- 比较取出前后列表的差值。
优点:
- redis读写纯内存操作,性能较好。
- zset每次add即可,无需判断是否存在,并且纯天然支持排序。
代码演示:
redisTemplate.opsForZSet().add(key, value, scoreVal)
key自定义,value必须唯一,如客户端ID,scoreVal表示客户端的排序值,如客户端等级。这样每次上报数据都实时更新,动态实现排序。 下面取出top N的数据:
redisTemplate.opsForZSet().reverseRangeWithScores(key, 0, N - 1);
取出top N后,要比较差集,这个可以用java api实现,但是这里可以借助redis的set方法difference来实现。 首先上面得到 top N集合后,写入到一个临时的集合Set中,假如它的key为tmpKey:
redisTemplate.opsForSet().add(tmpKey, top N集合)
假如旧的集合为key,它也是set结构的,那么要得到上面要被移除的C客户端,可以这样:
redisTemplate.opsForSet().difference(key, tmpKey)
要得到上面新加入的 D客户端,可以这样:
redisTemplate.opsForSet().difference(tmpKey, key)
最终将旧的集合key换为最新的top N的数据,实现如下:
#先从key集合中移除掉C redisTemplate.opsForSet().remove(key, 被移除的C) //这里可批量删,value支持数组 #再对key和tmpKey集合元素合并写到key中,即 redisTemplate.opsForSet().unionAndStore(key, tmpKey, key)
这时集合key中的元素就是最新的: A ,B, D 下一个定时任务周期,继续以上操作。。。 注意:上面临时tmpKey每次循环之前都要清空,最好再设置个过期时间,以免一直占用redis内存。
这里还有一个问题: 上面用zset存储的value必须为唯一对象,一般都是ID之类的,但是我们会有这样的需求:不仅仅只有ID,可能还有名称、当前等级这些附加属性。 这时怎么办,不可能把整个对象放到zset的value中,因为这样每次都是不同的value了,做不到更新score使其动态排序。
怎么办? 我们可以再利用redis的另外一个数据结构hash来帮我们实现,hash结构专门负责存储附加属性的值。zset的value依然是id,当我们要去拿这个id的附加属性值,可以去hash这边拿回来,接着做后续的处理逻辑。 这个hash结构我们可以这样设计: hashKey: 自定义 key: id 对应zset的 value value:附加属性的各种信息
上一篇:
微信小程序ssm电影院售票系统app
下一篇:
C语言成绩管理分析系统