《Redis实战》学习实践
序:
最近急需学习Redis相关内容,因此阅读了一本相当不错的书籍:《Redis实战》,在这里记录学习的过程以及遇到的问题。
一、第一章 根据文章点赞及发布日期list文章
业务:
全部代码示例:
import redis.clients.jedis.Jedis; import redis.clients.jedis.ZParams; import java.util.*; public class Chapter01 { private static final int ONE_WEEK_IN_SECONDS = 7 * 86400; private static final int VOTE_SCORE = 432; private static final int ARTICLES_PER_PAGE = 25; public static final void main(String[] args) { new Chapter01().run(); } public void run() { Jedis conn = new Jedis("localhost"); conn.select(15); String articleId = postArticle( conn, "username", "A title", "http://www.google.com"); System.out.println("We posted a new article with id: " + articleId); System.out.println("Its HASH looks like:"); Map<String,String> articleData = conn.hgetAll("article:" + articleId); for (Map.Entry<String,String> entry : articleData.entrySet()){ System.out.println(" " + entry.getKey() + ": " + entry.getValue()); } System.out.println(); articleVote(conn, "other_user", "article:" + articleId); String votes = conn.hget("article:" + articleId, "votes"); System.out.println("We voted for the article, it now has votes: " + votes); assert Integer.parseInt(votes) > 1; System.out.println("The currently highest-scoring articles are:"); List<Map<String,String>> articles = getArticles(conn, 1); printArticles(articles); assert articles.size() >= 1; addGroups(conn, articleId, new String[]{"new-group"}); System.out.println("We added the article to a new group, other articles include:"); articles = getGroupArticles(conn, "new-group", 1); printArticles(articles); assert articles.size() >= 1; } public String postArticle(Jedis conn, String user, String title, String link) { String articleId = String.valueOf(conn.incr("article:")); String voted = "voted:" + articleId; conn.sadd(voted, user); conn.expire(voted, ONE_WEEK_IN_SECONDS); long now = System.currentTimeMillis() / 1000; String article = "article:" + articleId; HashMap<String,String> articleData = new HashMap<String,String>(); articleData.put("title", title); articleData.put("link", link); articleData.put("user", user); articleData.put("now", String.valueOf(now)); articleData.put("votes", "1"); conn.hmset(article, articleData); conn.zadd("score:", now + VOTE_SCORE, article); conn.zadd("time:", now, article); return articleId; } public void articleVote(Jedis conn, String user, String article) { long cutoff = (System.currentTimeMillis() / 1000) - ONE_WEEK_IN_SECONDS; if (conn.zscore("time:", article) < cutoff){ return; } String articleId = article.substring(article.indexOf(':') + 1); if (conn.sadd("voted:" + articleId, user) == 1) { conn.zincrby("score:", VOTE_SCORE, article); conn.hincrBy(article, "votes", 1); } } public List<Map<String,String>> getArticles(Jedis conn, int page) { return getArticles(conn, page, "score:"); } public List<Map<String,String>> getArticles(Jedis conn, int page, String order) { int start = (page - 1) * ARTICLES_PER_PAGE; int end = start + ARTICLES_PER_PAGE - 1; Set<String> ids = conn.zrevrange(order, start, end); List<Map<String,String>> articles = new ArrayList<Map<String,String>>(); for (String id : ids){ Map<String,String> articleData = conn.hgetAll(id); articleData.put("id", id); articles.add(articleData); } return articles; } public void addGroups(Jedis conn, String articleId, String[] toAdd) { String article = "article:" + articleId; for (String group : toAdd) { conn.sadd("group:" + group, article); } } public List<Map<String,String>> getGroupArticles(Jedis conn, String group, int page) { return getGroupArticles(conn, group, page, "score:"); } public List<Map<String,String>> getGroupArticles(Jedis conn, String group, int page, String order) { String key = order + group; if (!conn.exists(key)) { ZParams params = new ZParams().aggregate(ZParams.Aggregate.MAX); conn.zinterstore(key, params, "group:" + group, order); conn.expire(key, 60); } return getArticles(conn, page, key); } private void printArticles(List<Map<String,String>> articles){ for (Map<String,String> article : articles){ System.out.println(" id: " + article.get("id")); for (Map.Entry<String,String> entry : article.entrySet()){ if (entry.getKey().equals("id")){ continue; } System.out.println(" " + entry.getKey() + ": " + entry.getValue()); } } } }
对文章进行分组:
群组功能由两个部分组成,一个部分负责记录文章属于哪个群组,另一个部分负责取出群组里的文章。为了记录群组里都保存了哪些文章,网站需要为每个群组创建一个集合,并将所有同属于一个群组的文章ID都记录到那个集合里。
package com.ual; import redis.clients.jedis.Jedis; import redis.clients.jedis.ZParams; import java.util.*; public class ArticleService { //设置过期时间 private static final int One_Week_In_Seconds=7*86400; //设置评分常量:一天的秒数除以200(默认热门文章点赞数) private static final int Vote_Score=432; //设置分页展示文章数量 private static final int Article_Pre_Page=25; /*发布并获取文章*/ public String postArticle(Jedis conn,String user,String title,String link){ //设置文章id, String articleId=String.valueOf(conn.incr("article:")); //创建指定文章点赞用户(用于防止重复点赞) String voted="voted:"+articleId; conn.sadd(voted,user); //设置点赞用户表的过期时间 conn.expire(voted,One_Week_In_Seconds); long now=System.currentTimeMillis()/1000; String article="article:"+articleId; //文章数据存储在hash表中 HashMap<String ,String> articleData=new HashMap<>(); articleData.put("id",articleId); articleData.put("title",title); articleData.put("link",link); articleData.put("user",user); articleData.put("time",String.valueOf(now)); articleData.put("votes","0"); conn.hmset(article,articleData); conn.zadd("score:",now+Vote_Score,article); conn.zadd("time:",now,article); return articleId; } public void articleVote(Jedis conn,String user,String article){ //设置过期时间 long cutoff=(System.currentTimeMillis()/1000)-One_Week_In_Seconds; //如果文章已经发表七天了,点赞无效 if(conn.zscore("time:",article)<cutoff){ return; } String articleId=article.substring(article.indexOf(":")+1); if(conn.sadd("voted:"+articleId,user)==1){ //score表中article分值加432 conn.zincrby("score:",Vote_Score,article); //article表中votes字段加1 conn.hincrBy(article,"votes",1); } } public List<Map<String,String>> getArticle(Jedis conn,int page,String order){ //定义分页开始结束序号 int start=(page-1)*Article_Pre_Page; int end=start+Article_Pre_Page-1; //从order(time:或score:)中取出全部文章id Set<String> ids=conn.zrevrange(order,start,end); //定义一个链表,存储要取出的全部文章 List<Map<String,String>> articles=new ArrayList<>(); for(String id:ids){ //从对应文章表中取出文章数据(hgetAll为取出全部字段) Map<String,String> articleData=conn.hgetAll(id); articles.add(articleData); } return articles; } //添加分组分组 一篇文章可以属于多个群组 public void addGroups(Jedis conn,String articleId,String[] toadd){ String article="article:"+articleId; for (String group:toadd){ conn.sadd("group"+group,article); } } public List<Map<String,String>> getGroupArticles(Jedis conn,String group,int page,String order){ //为每个群组的每种排列顺序都创建一个键 String key=order+group; if(!conn.exists(key)){ ZParams params=new ZParams().aggregate(ZParams.Aggregate.MAX); //获取分组文章与排序文章id的交集存入key中,key中的排序方式依据排序文章的方式进行排序 conn.zinterstore(key,params,"group:"+group,order); //设置过期时间为60s conn.expire(key,60); } return getArticle(conn,page,key); } public void printArticles(List<Map<String,String>> articles){ for(Map<String,String> article:articles){ System.out.println("id:"+article.get("id")); for(Map.Entry<String,String> entry:article.entrySet()){ if(entry.getKey().equals("id")){ continue; } System.out.println(" "+entry.getKey()+" : "+entry.getValue()); } } } }
Redis不仅可以对多个多个集合执行操作,甚至在一些情况下,还可以在集合和有序集合之间执行操作。
为了能根据评分对群组文章进行排序和分页,网站需要将同一个群组里的所有文章都按照评分有序的存储到一个有序的集合里(设置60秒后就过期),Redis的ZINTERSTORE命令可以j接受多个集合和多个有序集合作为输入,找出所有同时存在于集合和有序集合的成员,并以几种不同的方式来合并这些成员的分值(即没有排序规则的集合成员的分值都会被视为1,在这里指的是普通分组成员)。对于我们的文章投票网站来说,程序需要使用ZINTERSTORE命令选出相同成员中最大的那个分值来作为交集的分值:取决于所使用的排序选项,这些分值既可以是文字的评分,也可以是文字的发布时间。
第二章
1)登陆和cookie缓存