Redis是一个高效的内存数据库,它支持包括String、List、Set、SortedSet和Hash等数据类型的存储,在Redis中通常根据数据的key查询其value值,Redis没有条件查询,在面对一些需要分页或排序的场景时(如评论,时间线),Redis就不太好不处理了。
前段时间在项目中需要将每个主题下的用户的评论组装好写入Redis中,每个主题会有一个topicId,每一条评论会和topicId关联起来,得到大致的数据模型如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| {
topicId: 'xxxxxxxx',
comments: [
{
username: 'niuniu',
createDate: 1447747334791,
content: '在Redis中分页',
commentId: 'xxxxxxx',
reply: [
{
content: 'yyyyyy'
username: 'niuniu'
},
...
]
},
...
]
}
|
将评论数据从MySQL查询出来组装好存到Redis后,以后每次就可以从Redis获取组装好的评论数据,从上面的数据模型可以看出数据都是key-value型数据,无疑要采用hash进行存储,但是每次拿取评论数据时需要分页而且还要按createDate字段进行排序,hash肯定是不能做到分页和排序的。
那么,就挨个看一下Redis所支持的数据类型:
1、String: 主要用于存储字符串,显然不支持分页和排序。
2、Hash: 主要用于存储key-value型数据,评论模型中全是key-value型数据,所以在这里Hash无疑会用到。
3、List: 主要用于存储一个列表,列表中的每一个元素按元素的插入时的顺序进行保存,如果我们将评论模型按createDate排好序后再插入List中,似乎就能做到排序了,而且再利用List中的LRANGE key start stop指令还能做到分页。嗯,到这里List似乎满足了我们分页和排序的要求,但是评论还会被删除,就需要更新Redis中的数据,如果每次删除评论后都将Redis中的数据全部重新写入一次,显然不够优雅,效率也会大打折扣,如果能删除指定的数据无疑会更好,而List中涉及到删除数据的就只有LPOP和RPOP这两条指令,但LPOP和RPOP只能删除列表头和列表尾的数据,不能删除指定位置的数据,所以List也不太适合(转载的时候看了下,是有 LREM命令可以做到删除,但是LRANGE 似乎是一个耗时命令 O(N) )。
4、Set: 主要存储无序集合,无序!排除。
5、SortedSet: 主要存储有序集合,SortedSet的添加元素指令ZADD key score member [[score,member]…]会给每个添加的元素member绑定一个用于排序的值score,SortedSet就会根据score值的大小对元素进行排序,在这里就可以将createDate当作score用于排序,SortedSet中的指令ZREVRANGE key start stop又可以返回指定区间内的成员,可以用来做分页,SortedSet的指令ZREM key member可以根据key移除指定的成员,能满足删评论的要求,所以,SortedSet在这里是最适合的(时间复杂度O(log(N)))。
所以,我需要用到的数据类型有SortSet和Hash,SortSet用于做分页排序,Hash用于存储具体的键值对数据,我画出了如下的结构图:

在上图的SortSet结构中将每个主题的topicId作为set的key,将与该主题关联的评论的createDate和commentId分别作为set的score和member,commentId的顺序就根据createDate的大小进行排列。
当需要查询某个主题某一页的评论时,就可主题的topicId通过指令zrevrange topicId (page-1)×10 (page-1)×10+perPage这样就能找出某个主题下某一页的按时间排好顺序的所有评论的commintId。page为查询第几页的页码,perPage为每页显示的条数。
当找到所有评论的commentId后,就可以把这些commentId作为key去Hash结构中去查询该条评论对应的内容。
这样就利用SortSet和Hash两种结构在Redis中达到了分页和排序的目的。
博主额外添加的实现算法:
- @Test
- public void sortedSetPagenation(){
- for ( int i = 1 ; i <= 100 ; i+=10) {
-
- RedisClient.zadd("topicId", i, "commentId"+i);
-
- RedisClient.hset("Comment_Key","commentId"+i, "comment content .......");
- }
-
- LinkedHashSet<String> sets = RedisClient.zrevrangebyscore("topicId", "80", "1", 0, 5);
- String[] items = new String[]{};
- System.out.println(sets.toString());
-
- List<String> list = RedisClient.hmget("Comment_Key", sets.toArray(items));
- for(String str : list){
- System.out.println(str);
- }
- }
工具类:
- package com.util;
-
- import java.util.LinkedHashSet;
- import java.util.List;
-
- import redis.clients.jedis.Jedis;
- import redis.clients.jedis.JedisPool;
- import redis.clients.jedis.JedisPoolConfig;
-
-
-
-
-
-
-
- public class RedisClient{
-
- private static JedisPool jedisPool;
-
- static {
- JedisPoolConfig config = new JedisPoolConfig();
- config.setMaxTotal(Global.MAX_ACTIVE);
- config.setMaxIdle(Global.MAX_IDLE);
- config.setMaxWaitMillis(-1);
- config.setTestOnBorrow(Global.TEST_ON_BORROW);
- config.setTestOnReturn(Global.TEST_ON_RETURN);
- jedisPool = new JedisPool("redis://:"+Global.REDIS_SERVER_PASSWORD+"@"+Global.REDIS_SERVER_URL+":"+Global.REDIS_SERVER_PORT);
-
- }
-
- public static String set(String key, String value) {
- Jedis jedis = jedisPool.getResource();
- String result = jedis.set(key, value);
- jedis.close();
- return result;
- }
-
- public static String get(String key) {
- Jedis jedis = jedisPool.getResource();
- String result = jedis.get(key);
- jedis.close();
- return result;
- }
-
- public static Long hset(String key, String item, String value) {
- Jedis jedis = jedisPool.getResource();
- Long result = jedis.hset(key, item, value);
- jedis.close();
- return result;
- }
-
- public static String hget(String key, String item) {
- Jedis jedis = jedisPool.getResource();
- String result = jedis.hget(key, item);
- jedis.close();
- return result;
- }
-
-
-
-
-
-
-
-
- public static List<String> hmget(String key, String... item) {
- Jedis jedis = jedisPool.getResource();
- List<String> result = jedis.hmget(key, item);
- jedis.close();
- return result;
- }
-
- public static Long incr(String key) {
- Jedis jedis = jedisPool.getResource();
- Long result = jedis.incr(key);
- jedis.close();
- return result;
- }
-
- public static Long decr(String key) {
- Jedis jedis = jedisPool.getResource();
- Long result = jedis.decr(key);
- jedis.close();
- return result;
- }
-
- public static Long expire(String key, int second) {
- Jedis jedis = jedisPool.getResource();
- Long result = jedis.expire(key, second);
- jedis.close();
- return result;
- }
-
- public static Long ttl(String key) {
- Jedis jedis = jedisPool.getResource();
- Long result = jedis.ttl(key);
- jedis.close();
- return result;
- }
-
- public static Long hdel(String key, String item) {
- Jedis jedis = jedisPool.getResource();
- Long result = jedis.hdel(key, item);
- jedis.close();
- return result;
- }
-
- public static Long del(String key) {
- Jedis jedis = jedisPool.getResource();
- Long result = jedis.del(key);
- jedis.close();
- return result;
- }
-
- public static Long rpush(String key, String... strings) {
- Jedis jedis = jedisPool.getResource();
- Long result = jedis.rpush(key, strings);
- jedis.close();
- return result;
- }
-
-
-
-
-
-
-
-
-
- public static List<String> lrange(String key, int start, int end) {
- Jedis jedis = jedisPool.getResource();
- List<String> result = jedis.lrange(key, start, end);
- jedis.close();
- return result;
- }
-
-
-
-
-
-
-
- public static Long lrem(String key, Long count, String value) {
- Jedis jedis = jedisPool.getResource();
- Long result = jedis.lrem(key, count, value);
- jedis.close();
- return result;
- }
-
-
-
-
-
-
-
-
-
-
-
-
- public static Long zadd(String key, double score, String member) {
- Jedis jedis = jedisPool.getResource();
- Long result = jedis.zadd(key, score, member);
- jedis.close();
- return result;
- }
-
-
-
-
-
-
-
-
-
-
-
-
- public static LinkedHashSet<String> zrevrangebyscore(String key, String max, String min, int offset, int count){
- Jedis jedis = jedisPool.getResource();
- LinkedHashSet<String> result = (LinkedHashSet<String>) jedis.zrevrangeByScore(key, max, min, offset, count);
- jedis.close();
- return result;
- }
-
- }