构建一个文章投票网站,一般具备下面几个功能
发布文章
文章投票评分(按投票多少进行评分)
文章排序(按发布时间,按评分高低)
文章分组(如专题)
...
1.关系型数据库设计
其中用户,组两个表简单化处理了。业务实现起来也相当简单。不再赘述。重点是如何使用redis实现类似的业务逻辑。
由于redis是基于key-value管理,属于列式数据库。和关系型数据库实现方式差异较大,值得研究。
redis的设计,最重要的一部分工作就是key的命名以及键值数据类型的选择上。
2.Redis设计
关系型数据库属于二维,数据关系主要通过在行和列两者说明,而redis中的数据关系,则通过key键值描述,所以要求redis键值具备层次性。
2.1文章发布
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | private static final int ONE_WEEK_IN_SECONDS = 7 * 86400 ; private static final int VOTE_SCORE = 432 ; 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; } |
2.2文章投票
实现代码
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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" , 1l); } } |
2.3返回文章列表
两种排序策略:按发布时间,按文章评分。
支持分页排序。
redis的实现排序方式和关系型数据库中的实现方式有很大差别,这也是key-value数据库的一大特点。
基于key操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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; // 从排序集合中获取 id 列表 Set<String> ids = conn.zrevrange(order, start, end); List<Map<String,String>> articles = new ArrayList<Map<String,String>>(); // 遍历 id 列表,逐条初始化 for (String id : ids){ Map<String,String> articleData = conn.hgetAll( id ); articleData.put( "id" , id ); articles.add(articleData); } // 注意:返回的信息中,没有列表总数 return articles; } |
2.4 文章分组
这一块逻辑相对独立,仅仅是文章的一个分析维度而已,操作起来相对简单。就是维护groups:${group}集合。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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; //60 秒的有效期 if (!conn.exists(key)) { ZParams params = new ZParams().aggregate(ZParams.Aggregate.MAX); conn.zinterstore(key, params, "group:" + group, order); // 有序集合,与group的交集,生成新的集合 conn.expire(key, 60); //60 秒的有效期,性能和实时性的平衡,需要具体情况具体分析 } return getArticles(conn, page, key); } |
zinterstore API
public java.lang.Long zinterstore(java.lang.String dstkey,
redis.clients.jedis.ZParams params,
java.lang.String... sets)
本文转自 randy_shandong 51CTO博客,原文链接:http://blog.51cto.com/dba10g/1853019,如需转载请自行联系原作者