Redis在文章投票場景的設計

需求

1、文章資訊包括:標題、內容、連結、釋出時間、釋出人,釋出後就給自己投一張票。

2、一個人只能給一篇文章投一張票。

3、文章只能七天內允許投票。

4、文章投票排名考慮文章釋出時間以及投票數量,以釋出時間作為降序排序,如果投票數量為200,則時間向前移一天。

5、文章排序包括髮布時間排序,投票得分排序

分析

第一個需求

可以採用雜湊來儲存文章資訊。存入資訊用HMSET,取出對應欄位資訊用HMGET,取出key的所有資訊用HGETALL,基本用法如下:

// 賦值local:0>hmset person:001 name ‘張三’ age 18“OK”// 取對應key的某個屬性值 local:0>hmget person:001 name1) “張三”// 取對應key的所有屬性值local:0>hgetall person:0011) “name”2) “張三”3) “age”4) “18”

第二個需求

這邊考慮到不能重複的概念,在java裡用set,redis也有集合的概念。集合簡單的說,就是不能有重複的資料,基本用法如下:

// 賦值local:0>sadd name ‘張三’“1”// 賦值local:0>sadd name ‘李四’“1”// 賦值失敗,返回0,因為已經新增過local:0>sadd name ‘張三’“0”// 獲取對應key的所有成員local:0>smembers name1) “張三”2) “李四”

第三個需求

有時間控制需求,需要把文章id和釋出時間儲存起來,考慮到後面用時間進行排序,所以用有序集合。有序集合和上面集合不一樣的是,多了一個分值的概念,可以透過分值進行排序等操作。七天後不能投票就可以透過這個分值來計算。

// 新增local:0>zadd score 88 ‘趙大’“1”// 新增local:0>zadd score 93 ‘熊二’“1”// 新增local:0>zadd score 92 ‘張三’“1” // 新增local:0>zadd score 89 ‘李四’“1”// 新增local:0>zadd score 70 ‘王五’“1”// 重複新增失敗返回0,但是這邊做了修改local:0>zadd score 60 ‘王五’“0”// 分數加5,返回最終值local:0>zincrby score 5 ‘王五’“65”// 從低到高排序,取前四個local:0>zrange score 0 3 withscores1) “王五”2) “65”3) “趙大”4) “88”5) “李四”6) “89”7) “張三”8) “92”// 從高到低排序,取前四個local:0>zrevrange score 0 3 withscores1) “熊二”2) “93”3) “張三”4) “92”5) “李四”6) “89”7) “趙大”8) “88”// 獲取分數值local:0>zscore score ‘張三’“92”

第四個需求

一天有84600秒,200票時間向前移動一天,則每票就是84600/200=432分。

第五個需求

投票要根據分數來排序,所以同需求三,用有序集合。

實踐

釋出文章

private static final int ONE_WEEK_IN_SECONDS = 7 * 86400;private static final int VOTE_SCORE = 432;@Testpublic void postArticle() throws InterruptedException { for (int i = 0; i < 5; i++) { // 透過incre獲取自增長主鍵 String articleId = String。valueOf(JedisUtils。incre(“article:”)); // 定義主鍵 String article = “article:” + articleId; long now = System。currentTimeMillis() / 1000; String user = “釋出人” + i; // 設定文章 Map articleMap = new HashMap<>(); articleMap。put(“title”, “文章標題” + i); articleMap。put(“content”, “文章內容” + i); articleMap。put(“link”, “文章連結” + i); articleMap。put(“user”, user); articleMap。put(“now”, String。valueOf(now)); // 記錄投票數 articleMap。put(“votes”, “1”); // 記錄投票人,防止重複投票,設定過期時間 String voted = “voted:” + articleId; JedisUtils。sadd(voted, user); JedisUtils。expire(voted, ONE_WEEK_IN_SECONDS); // 透過文章主鍵插入 JedisUtils。hmset(article, articleMap); //需要時間和分數排序 JedisUtils。zadd(“score:”, now + VOTE_SCORE, article); JedisUtils。zadd(“time:”, now, article); TimeUnit。SECONDS。sleep(1); }}

投票

@Testpublic void voteArticle() { for (int i = 1; i < 3; i++) { String article = “article:” + i; String voted = “voted:” + i; System。out。println(“投票前,第一篇文章和第二篇文章的投票數:”); System。out。println(“文章資訊:” + getArticle(article)); System。out。println(“投票資訊:” + getVotesUser(voted)); } String user = “釋出人1”; for (int i = 1; i < 3; i++) { String article = “article:” + i; String voted = “voted:” + i; // 獲取釋出時間 Double zscore = JedisUtils。zscore(“time:”, article); long now = System。currentTimeMillis() / 1000; // 失效了,不能投票 if (zscore < (now - ONE_WEEK_IN_SECONDS)) { continue; } // 返回0說明已經存在沒有插入 if (JedisUtils。sadd(voted, user) == 0) { continue; } // 沒有返回0怎插入成功,順便更新分數 JedisUtils。zincrby(“score:”, VOTE_SCORE, article); // 更新文章投票數 JedisUtils。hincrBy(article, “votes”, 1); } for (int i = 1; i < 3; i++) { String article = “article:” + i; String voted = “voted:” + i; System。out。println(“投票後,第一篇文章和第二篇文章的投票數:”); System。out。println(“文章資訊:” + getArticle(article)); System。out。println(“投票資訊:” + getVotesUser(voted)); }}private Map getArticle(String article) { Map articleMap = JedisUtils。hgetAll(article); return articleMap;}private Set getVotesUser(String key) { return JedisUtils。smembers(key);}

執行結果如下,可以看到,文章1被投了一票,文章2由於是作者,已經投過票了,所以不能投票

Redis在文章投票場景的設計

排序

@Testpublic void sortArticle() { // 根據時間排序 System。out。println(JedisUtils。zrevrange(“score:”, 0, -1)); // 根據分值排序 System。out。println(JedisUtils。zrevrange(“time:”, 0, -1));}

執行結果如下,第一行結果,由於文章1被投了一票,雖然釋出時間比文章5早,但是拍在文章5前面。第二行結果就是按時間排序的。

Redis在文章投票場景的設計