天天看點

從零開始搭建部落格03----本周熱議處理(redis 有序清單處理)

本周熱議,本周發表并且評論最多的文章排行,如果直接查詢資料庫的話很快就可以實作,隻需要限定一下文章建立時間,然後根據評論數量倒叙取前幾篇即可搞定。

但這裡我們使用redis來完成。之前上課時候我們說過,排行榜功能,我們可以使用redis的有序集合zset來完成。現在我們就這個資料結構來完成本周熱議的功能。

在編碼之前,我們需要先來回顧一下zset的幾個基本指令。

zrange key start stop [WITHSCORES]

withscores代表的是否顯示順序号 start和stop代表所在的位置的索引。可以這樣了解:将集合元素依照順序值升序排序再輸出,start和stop限制周遊的限制範圍

zincrby key increment member

為有序集 key 的成員 member 的 score 值加上增量 increment 。

ZUNIONSTORE destination numkeys key [key …] [WEIGHTS weight [weight …]] [AGGREGATE SUM|MIN|MAX]

計算給定的一個或多個有序集的并集,其中給定 key 的數量必須以 numkeys 參數指定,并将該并集(結果集)儲存到 destination 。

預設情況下,結果集中某個成員的 score 值是所有給定集下該成員 score 值之 和

實作步驟

  1. 查庫擷取最近7天的所有評論數量大于 0文章
  2. 把文章的評論數量作為有序集合的分數,文章id作為id存儲到zset中
  3. 緩存文章到set中,評論數量作為排行标準
  4. 設定有效期為7天,因為超過了7天也就失去了時效性

具體代碼實作

com.fly.service.impl.PostServiceImpl#initIndexWeekRank

@Override
    public void initIndexWeekRank() {
//        查庫擷取最近7天的所有文章
        List<Post> last7DayPosts = this.list(new QueryWrapper<Post>()
                .ge("created", DateUtil.offsetDay(new Date(), -7).toJdkDate())
                .gt("comment_count", 0)
                .select("id, title, user_id, comment_count, view_count, created"));
//        然後把文章的評論數量作為有序集合的分數,文章id作為ID存儲到zset中。
        for (Post post : last7DayPosts) {
            String key = "day_rank" + DateUtil.format(post.getCreated(), DatePattern.PURE_DATE_PATTERN);
//            設定有效期,7天之内有效
            long between = DateUtil.between(new Date(), post.getCreated(), DateUnit.DAY);
            long expireTime = (7 - between) * 24 * 60 * 60;

//            緩存文章到set中,評論數量作為排行标準
            redisUtil.zSet(key, post.getId(), post.getCommentCount());
            //設定有效期
            redisUtil.expire(key, expireTime);
//            緩存文章基本資訊(hash結構)
            this.hashCachePostIdAndTitle(post);

        }

//        7天閱讀相加
        this.zUnionAndStroreLast7DaysForWeekRand();

    }      

因為要顯示文章的标題等基本資訊

/**
     * hash結構緩存文章标題和id
     *
     * @param post
     */
    private void hashCachePostIdAndTitle(Post post) {
        boolean isExist = redisUtil.hasKey("rank_post_" + post.getId());
        if (!isExist) {
            long between = DateUtil.between(new Date(), post.getCreated(), DateUnit.DAY);
            long expireTime = (7 - between) * 24 * 60 * 60;

//            緩存文章基本資訊
            redisUtil.hset("rank_post_" + post.getId(), "post:id", post.getId(), expireTime);
            redisUtil.hset("rank_post_" + post.getId(), "post:title", post.getTitle(), expireTime);
        }
    }

    /**
     * 把最近7天的文章評論數量統計一下
     * 用于首頁的7天評論排行榜
     */
    public void zUnionAndStroreLast7DaysForWeekRand() {
        String prifix = "day_rank";

        List<String> keys = new ArrayList<>();
        String key = prifix + DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN);

        for (int i = -7; i < 0; i++) {
            Date date = DateUtil.offsetDay(new Date(), i).toJdkDate();
            keys.add(prifix + DateUtil.format(date, DatePattern.PURE_DATE_PATTERN));
        }

        redisUtil.zUnionAndStore(key, keys, "last_week_rank");

    }      

我們在項目啟動之時初始化,代碼如下:

從零開始搭建部落格03----本周熱議處理(redis 有序清單處理)

增加評論數量

使用者發表評論之後應該要資料庫中的comment_count 加1,也要讓緩存中數量加1,這樣才能保證資料的一緻性,在com.fly.controller.PostController#commentAdd 中

從零開始搭建部落格03----本周熱議處理(redis 有序清單處理)
@Override
    public void incrZsetValueAndUnionForLastWeekRank(Long postId) {
        String dayRank = "day_rank" + DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN);
//        文章閱讀加一
        redisUtil.zIncrementScore(dayRank, postId, 1);
        this.hashCachePostIdAndTitle(this.getById(postId));

//      重新union最近7天
        this.zUnionAndStroreLast7DaysForWeekRand();
    }      

其實邏輯也和初始化差不多,首先給文章數量加一。集合名稱是對應當天的。比如今天是12月21,對應的key就是day_rank:20181221。在這個上面給對應的文章id加一。這樣每天都有評論這篇文章的話,我們再做交集處理,把每天的評論數量都加起來。得到的就是總的評論數量了。

<!--本周熱議-->
<div th:fragment="weekPopular">
    <dl class="fly-panel fly-list-one" id="post-hots">
        <dt class="fly-panel-title">本周熱議</dt>
        <script id="hostDd" type="text/html">
            {{#  layui.each(d.list, function(index, item){ }}
            <dd>
                <a href="/post/{{item.id}}">{{item.title}}</a>
                <span><i class="iconfont icon-pinglun1"></i>{{item.comment_count}}</span>
            </dd>
            {{#  }); }}
            {{#  if(d.list.length === 0){ }}
            <div class="fly-none">沒有相關資料</div>
            {{#  } }}
        </script>
    </dl>
    <script>
        layui.use(['laytpl'], function () {
            var laytpl = layui.laytpl;

            var post_hosts = document.getElementById("post-hots");
            var tpl = document.getElementById("hostDd").innerHTML;
            var data = {};
            $.ajax({
                url: "/post/hosts",
                async: false,
                success: function (res) {
                    if (res.code == 0) {
                        data.list = res.data;
                    }
                }
            });
            laytpl(tpl).render(data, function (html) {
                post_hosts.innerHTML += html;

            });

        });

    </script>
</div>      

繼續閱讀