Redis从基础命令到实战之有序集合类型(SortedSet)
有序集合类型是Redis五种数据类型中最高级的、也是最复杂的类型。有序集合具有集合类型的特性,在其基础上给每个元素关联了一个分值,或称为权重,操作时既可以在添加元素时指定分值,也可以单独修改集合中某一个元素的分值。使用时可以按分值排序(从低到高或从高到低)并顺序读取全部或某一范围内的元素,或者获得某一分值范围内的元素。利用这一特点,可以很方便的实现排行榜、热门文章等功能。
在本节的实例中,使用有序集合类型为商品管理模块添加一个按浏览量排序的功能。
一、常用命令
import java.util.Set; import redis.clients.jedis.Jedis; import redis.clients.jedis.Tuple; public class SortedSetExample { public static void main(String[] args) { Jedis jedis = JedisProvider.getJedis(); jedis.flushDB(); // 向有序集合中加入元素, 成功返回1, 失败返回0 Long zadd = jedis.zadd("fruit", 5.0, "apple"); print("zadd fruit apple=" + zadd); // 重复添加, 会修改权重 zadd = jedis.zadd("fruit", 6.0, "apple"); print("zadd fruit apple again=" + zadd); jedis.zadd("fruit", 2.0, "banana"); jedis.zadd("fruit", 4.0, "orange"); jedis.zadd("fruit", 8.0, "grape"); jedis.zadd("fruit", 10.0, "lemon"); jedis.zadd("fruit", 7.0, "cherry"); // 统计元素个数 print("zcard fruit=" + jedis.zcard("fruit")); // 统计某个权重范围内元素个数 print("zcount fruit [1.0,5.0]=" + jedis.zcount("fruit", 1.0, 5.0)); // 查看排名 print("zrank fruit grape=" + jedis.zrank("fruit", "grape")); print("zrevrank fruit grape=" + jedis.zrevrank("fruit", "grape")); // 按权重排序后读取索引范围内元素及权重 System.out.println("zrangeWithScores fruit"); Set<Tuple> tuples = jedis.zrangeWithScores("fruit", 0, -1); for (Tuple t : tuples) { System.out.println(t.getElement() + ":" + t.getScore()); } System.out.println("------------------------------------------------------"); System.out.println(); // 按权重反向排序后读取索引范围内元素及权重 System.out.println("zrevrangeWithScores fruit"); tuples = jedis.zrevrangeWithScores("fruit", 0, -1); for (Tuple t : tuples) { System.out.println(t.getElement() + ":" + t.getScore()); } System.out.println("------------------------------------------------------"); System.out.println(); // 按权重排序后读取索引范围内元素 Set<String> set = jedis.zrange("fruit", 1, 3); print("zrange fruit [1,3]=" + set); // 按权重反向排序后读取索引范围内元素 set = jedis.zrevrange("fruit", 1, 3); print("zrevrange fruit [1,3]=" + set); // 读取权重在指定范围内的元素及其权重并按权重排序 System.out.println("zrangeByScoreWithScores fruit [1.0,8.0]"); tuples = jedis.zrangeByScoreWithScores("fruit", 1.0, 8.0); for (Tuple t : tuples) { System.out.println(t.getElement() + ":" + t.getScore()); } System.out.println("------------------------------------------------------"); System.out.println(); // 读取权重在指定范围内的元素及其权重并按权重反向排序 System.out.println("zrevrangeByScoreWithScores fruit [8.0,1.0]"); tuples = jedis.zrevrangeByScoreWithScores("fruit", 8.0, 1.0); for (Tuple t : tuples) { System.out.println(t.getElement() + ":" + t.getScore()); } System.out.println("------------------------------------------------------"); System.out.println(); // 读取权重在指定范围内的元素并按权重排序 set = jedis.zrangeByScore("fruit", 1.0, 7.0); print("zrangeByScore fruit [1.0,7.0]=" + set); // 读取权重在指定范围内的元素并按权重反向排序 set = jedis.zrevrangeByScore("fruit", 7.0, 1.0); print("zrevrangeByScore fruit [7.0,1.0]=" + set); // 查看集合中指定元素的权重 double zscore = jedis.zscore("fruit", "grape"); print("zscore fruit grape=" + zscore); // 修改指定元素权重 print("zincrby fruit -2 grape=" + jedis.zincrby("fruit", -2, "grape")); print("zincrby fruit 5.0 lemon=" + jedis.zincrby("fruit", 5.0, "lemon")); print("after zincrby: zscore fruit grape=" + jedis.zscore("fruit", "grape")); // 删除元素 print("zrem fruit cherry=" + jedis.zrem("fruit", "cherry")); print("zremrangeByScore fruit [3.0,6.0]=" + jedis.zremrangeByScore("fruit", 3.0, 6.0)); jedis.close(); } private static void print(String info) { System.out.println(info); System.out.println("------------------------------------------------------"); System.out.println(); } }二、实践练习
在上一节的基础上,给每个商品增加一个浏览量的记录,商品每次被浏览,设置浏览量加1。
首先修改添加商品的代码,设置商品的初始浏览量为0。
/** * 添加一个商品 * @param goods * @return */ public boolean addGoods(Goods goods) { long id = getIncrementId(); Map<String, String> map = new HashMap<>(); map.put("id", String.valueOf(id)); map.put("title", goods.getTitle()); map.put("price", String.valueOf(goods.getPrice())); //随机获得三个分类 String size = SIZE[new Random().nextInt(SIZE.length)]; String color = COLOR[new Random().nextInt(COLOR.length)]; String key = "goods:" + id; boolean added = jedis.hmset(key, map).equals("OK"); if(added) { jedis.rpush("goods:list", String.valueOf(id)); //初始浏览量默认为0 jedis.zadd("goods:views", 0, String.valueOf(id)); //记录商品所属分类 jedis.sadd("goods:" + id + ":tags", size, color); //记录每个分类下包含的商品ID jedis.sadd("tag:" + size + ":goods", String.valueOf(id)); jedis.sadd("tag:" + color + ":goods", String.valueOf(id)); } return added; }用户浏览商品方法,浏览量加1。
/** * 浏览商品 * @param id */ public void view(long id) { jedis.zincrby("goods:views", 1, String.valueOf(id)); }按浏览量排序
/** * 读取商品列表并按浏览量排序 * @param pageIndex * @param pageSize * @return */ public List<Goods> getGoodsListOrderByViews(int pageIndex, int pageSize) { int totals = (int)getTotalCount(); int from = (pageIndex - 1) * pageSize; if(from < 0) { from = 0; } else if(from > totals) { from = (totals / pageSize) * pageSize; } int to = from + pageSize - 1; if(to > totals) { to = totals; } List<Goods> goodsList = new ArrayList<>(); Set<String> idList = jedis.zrevrange("goods:views", from, to); for(String id : idList) { String key = "goods:" + id; Map<String, String> maps = jedis.hgetAll(key); Goods goods = new Goods(); goods.setId(NumberUtils.toLong(maps.get("id"))); goods.setTitle(maps.get("title")); goods.setPrice(NumberUtils.toFloat(maps.get("price"))); //读取分类 goods.setTags(jedis.smembers("goods:" + id + ":tags")); goodsList.add(goods); } return goodsList; }测试代码
public static void main(String[] args) { SortedSetLession sl = new SortedSetLession(); sl.clear(); //添加一批商品 for(int i = 0; i< 42; i++) { Goods goods = new Goods(0, "goods" + String.format("%05d", (i + 1)), i); sl.addGoods(goods); } //读取商品总数 System.out.println("商品总数: " + sl.getTotalCount()); //删除第29件商品 sl.delGoods(29); System.out.println("删除后商品总数: " + sl.getTotalCount()); //分页显示 List<Goods> list = sl.getGoodsList(2, 20); System.out.println("第二页商品:"); for(Goods goods : list) { System.out.println(goods); } //按分类查找 List<Goods> listByTag = sl.findGoodsByTag("big", "green"); System.out.println("所有尺寸为big,颜色为green的商品:"); for(Goods goods : listByTag) { System.out.println(goods); } //模拟点击商品 sl.view(3); sl.view(3); sl.view(4); //按点击量排序 List<Goods> listWithOrder = sl.getGoodsListOrderByViews(1, 20); System.out.println("按点击量排序:"); for(Goods goods : listWithOrder) { System.out.println(goods); } }