Redis 实战学习(下)

231 阅读5分钟

这是我参与8月更文挑战的第4天,活动详情查看:8月更文挑战

一、实战

使用 jedis,测试环境:

  1. pom.xml 配置
  2. 测试代码

  1. pom.xml
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>3.5.2</version>
</dependency>
  1. 测试代码
public class JedisTest {

    private Jedis jedis;

    @Before
    public void setUp() {

        jedis = new Jedis("127.0.0.1", 6379);
    }

    @After
    public void after() {

        jedis.close();
    }

    @Test
    public void testCache() {

        // 最简单的设置缓存
        jedis.set("key1", "value1");

        System.out.println(jedis.get("key1"));
    }
}

(14)为商品搜索构建反向索引

实战操作如下:

  1. sadd:给商品添加一个关键词索引集合
  2. srem:删除商品
  3. smembers:获取一个商品所有的关键词
  4. sintern:对多个集合进行交集

实现如下:

public class JedisTest {
    /**
     * 添加商品的时候附带一些关键词
     * @param productId 商品Id
     * @param keywords 关键词
     */
    private void addProduct(long productId, String[] keywords) {
        for(String keyword : keywords) {
            jedis.sadd("keyword::" + keyword + "::products", String.valueOf(productId));
        }
    }
​
    /**
     * 根据多个关键词搜索商品
     * @param keywords 关键词
     * @return 商品
     */
    private Set<String> searchProduct(String[] keywords) {
        List<String> keywordSetKeys = new ArrayList<String>();
        for(String keyword : keywords) {
            keywordSetKeys.add("keyword::" + keyword + "::products");
        }
​
        String[] keywordArray = keywordSetKeys.toArray(new String[keywordSetKeys.size()]);
​
        return jedis.sinter(keywordArray);
    }
​
    @Test
    public void testProductSearch()  {
​
        // 添加一批商品
        addProduct(11, new String[]{"手机", "iphone", "潮流"});
        addProduct(12, new String[]{"iphone", "潮流", "炫酷"});
        addProduct(13, new String[]{"iphone", "天蓝色"});
​
        // 根据关键词搜索商品
        Set<String> searchResult = searchProduct(new String[]{"iphone", "潮流"});
        System.out.println("商品搜索结果为:" + searchResult);
    }
}

输出结果:

商品搜索结果为:[11, 12]

(15)实现音乐网站的排行榜程序

需求:按分数排序

主要使用数据结构:sorted set,不能有重复的数据,且排序。

实战操作如下:

  • zadd:把音乐加入排行榜中,刚开始分数可能就是0
  • zscore:可以获取音乐的分数
  • zrem:可以删除某个音乐
  • zincrby:可以给某个音乐增加分数
  • zrevrank:获取音乐在排行榜里的排名,zrevrange set 0 100 withscores 可以获取排名前100的热门歌曲

实现如下:

public class JedisTest {
    /**
     * 把新的音乐加入到排行榜里去
     * @param songId 音乐
     */
    private void addSong(long songId) {
        jedis.zadd("music_ranking_list", 0, String.valueOf(songId));
    }
​
    /**
     * 增加歌曲的分数
     * @param songId 音乐
     * @param score 分数
     */
    private void incrementSongScore(long songId, double score) {
        jedis.zincrby("music_ranking_list", score, String.valueOf(songId));
    }
​
    /**
     * 获取歌曲在排行榜里的排名
     * @param songId 音乐
     * @return 排名
     */
    private long getSongRank(long songId) {
        return jedis.zrevrank("music_ranking_list", String.valueOf(songId));
    }
​
    /**
     * 获取音乐排行榜
     * @return 排行榜
     */
    private Set<Tuple> getMusicRankingList() {
        return jedis.zrevrangeWithScores("music_ranking_list", 0, 2);
    }
​
    @Test
    public void testMusicRank()  {
​
        for(int i = 0; i < 20; i++) {
            addSong(i + 1);
        }
​
        incrementSongScore(5, 3.2);
        incrementSongScore(15, 5.6);
        incrementSongScore(7, 9.6);
​
        long songRank = getSongRank(5);
        System.out.println("查看id为5的歌曲的排名:" + (songRank + 1));
​
        Set<Tuple> musicRankingList = getMusicRankingList();
        System.out.println("查看音乐排行榜排名前3个的歌曲:" + musicRankingList);
    }
}

输出结果:

查看id为5的歌曲的排名:3
查看音乐排行榜排名前3个的歌曲:[[7,9.6], [15,5.6], [5,3.2]]

(16)实现一个新闻推荐机制

主要使用数据结构:sorted set,不能有重复的数据,且排序。

实战操作如下:

  • zadd:把当日最新的新闻加入到一个集合里
  • zcard:统计当日最新新闻
  • zcount:可以获取指定分数范围的数量
  • zrevrangebyscore max_time min_time start_index count withscores:按照时间分数进行倒序排序,然后获取指定的分页

实现如下:

public class JedisTest {
    /**
     * 加入一篇新闻
     * @param newsId 新闻Id
     */
    private void addNews(long newsId, long timestamp) {
        jedis.zadd("news", timestamp, String.valueOf(newsId));
    }
​
    /**
     * 搜索新闻
     * @param maxTimestamp 最大时间
     * @param minTimestamp 最小时间
     * @param index 索引
     * @param count 总数
     * @return 新闻
     */
    private Set<Tuple> searchNews(long maxTimestamp, long minTimestamp, int index, int count) {
        return jedis.zrevrangeByScoreWithScores("news", maxTimestamp, minTimestamp, index, count);
    }
​
    @Test
    public void testNews() {
​
        for(int i = 0; i < 20; i++) {
            addNews(i + 1, i + 1);
        }
​
        long maxTimestamp = 18;
        long minTimestamp = 2;
​
        int pageNo = 1;
        int pageSize = 10;
        int startIndex = (pageNo - 1) * 10;
​
        Set<Tuple> searchResult = searchNews(
                maxTimestamp, minTimestamp, startIndex, pageSize);
​
        System.out.println("搜索指定时间范围内的新闻的第一页:" + searchResult);
    }
}

输出结果:

搜索指定时间范围内的新闻的第一页:[[18,18.0], [17,17.0], [16,16.0], [15,15.0], [14,14.0], [13,13.0], [12,12.0], [11,11.0], [10,10.0], [9,9.0]]

(17)购买此商品的顾客也同时购买

主要使用数据结构:sorted set,不能有重复的数据,且排序。

实战操作如下:

  • zremrangebyscore:按照范围查询
  • zincrby:递增

实现如下:

public class JedisTest {
    /**
     * 继续购买商品
     * @param productId 商品Id
     * @param otherProductId 其他商品Id
     */
    private void continuePurchase(long productId, long otherProductId) {
        jedis.zincrby("continue_purchase_products::" + productId, 1, String.valueOf(otherProductId));
    }
​
    /**
     * 推荐其他人购买过的其他商品
     * @param productId 商品Id
     * @return 商品
     */
    private Set<Tuple> getRecommendProducts(long productId) {
        return jedis.zrevrangeWithScores("continue_purchase_products::" + productId, 0, 2);
    }
​
    @Test
    public void testProductRecommend() {
​
        int productId = 1;
​
        for(int i = 0; i < 20; i++) {
            continuePurchase(productId, i + 2);
        }
        for(int i = 0; i < 3; i++) {
            continuePurchase(productId, i + 2);
        }
​
        Set<Tuple> recommendProducts = getRecommendProducts(productId);
        System.out.println("推荐其他人购买过的商品:" + recommendProducts);
    }
}

输出结果:

推荐其他人购买过的商品:[[4,2.0], [3,2.0], [2,2.0]]

(18)网站搜索框的自动补全功能

需求:

对于输入的每个搜索词,都会遍历一下,拼接出来,然后 zincrby auto_complete:: 潜在搜索词 权重 完整搜索词,这样的话,就是每个潜在搜索词都会有一个集合,集合里是各种可能对应的搜索词和权重分数

查询:zrevrange set 0 9,潜在搜索词按照权重分数倒序拿出最近的10个搜索词,做一个自动提示和补全

实现如下:

public class JedisTest {
    /**
     * 搜索某个关键词
     * @param keyword 关键词
     */
    private void search(String keyword) {
        char[] keywordCharArray = keyword.toCharArray();
​
        StringBuffer potentialKeyword = new StringBuffer("");
        for(char keywordChar : keywordCharArray) {
            potentialKeyword.append(keywordChar);
​
            jedis.zincrby(
                    "potential_Keyword::" + potentialKeyword.toString() + "::keywords",
                    new Date().getTime(),
                    keyword);
        }
    }
​
    /**
     * 获取自动补全列表
     * @param potentialKeyword 潜在补全列表
     * @return 列表
     */
    private Set<String> getAutoCompleteList(String potentialKeyword) {
        return jedis.zrevrange("potential_Keyword::" + potentialKeyword + "::keywords",
                0, 2);
    }
​
    @Test
    public void testAutoComplete() {
​
        search("我爱大家");
        search("我喜欢学习Redis");
        search("我很喜欢一个城市");
        search("我不太喜欢玩儿");
        search("我喜欢学习Spark");
​
        Set<String> autoCompleteList = getAutoCompleteList("我");
        System.out.println("第一次自动补全推荐:" + autoCompleteList);
​
        autoCompleteList = getAutoCompleteList("我喜");
        System.out.println("第二次自动补全推荐:" + autoCompleteList);
    }
}

输出结果:

第一次自动补全推荐:[我喜欢学习Spark, 我很喜欢一个城市, 我不太喜欢玩儿]
第二次自动补全推荐:[我喜欢学习Spark, 我喜欢学习Redis]