Redis学习——举个栗子

原文地址:1.3 文章投票系统

1. 需求分析

要做一个可以对文章进行投票的系统,文章的排名跟得票数和发布时间相关,得票越多发布时间越近,排名越高。

一个简单的评分函数:score = post_time_in_seconds + votes * (1 * 24 * 60 *60) / 200

其中post_time_in_seconds是文章发布时间,取Unix time,即从1970年1月1日至发布时经过的秒数
votes为文章得票数,最后一项为每一票的权重。可以看出200票的权重等于一天的时间

效果类似于http://stackoverflow.com/

2. 存储结构设计

2.1 文章存储:

数据结构:hash
key: article:article_id
value:title: title, link: article_url, poster: user:user_id, time: post_time_in_seconds, votes:200

跟普通的关系数据库存储结构差不多

看个例子,图片源于原文: 以HASH结构存储文章的例子

2.2 时间排名的存储:

数据结构:zset
key: time:
value:'article:article_id': post_time_in_seconds

2.3 得分排名的存储:

数据结构:zset key: score:
value: 'article:article_id': score

类似对time和score两个字段分别做了索引

看个例子,图片源于原文: 以ZSET结构存储文章的排名

2.4 投票者的存储

数据结构:set
key: voted:article_id
value:user:user_id

记录一篇文章的所有投票者,用MySQL要更麻烦一些

看个例子,图片源于原文: 以ZSET结构存储文章的排名

2.5 分组信息的存储

数据结构:set
key: groups:group_name
value:article:article_id

看个例子,图片源于原文,score:programming是两个集合交叉的结果: 以ZSET结构存储文章的排名

3. 功能实现

ONE_WEEK_IN_SECONDS = 7 * 86400
VOTE_SCORE = 86400 / 200

# 给文章投票,当前仅支持投赞成票
def article_vote(conn, user, article):
    # vote time is one week
    cutoff = time.time() - ONE_WEEK_IN_SECONDS
    if conn.zscore('time:', article) < cutoff:
        return
    # if the user hasn't voted for this article before,
    # add the user to the voted:
    # increase this article's score
    # increase this article's votes count
    article_id = article.partition(':')[-1]
    if conn.sadd('voted:' + article_id, user):
        conn.zincrby('score:', article, VOTE_SCORE)
        conn.hincrby(article, 'votes', 1)

# 发表文章
def post_article(conn, user, title, link):
    # increase the article id
    article_id = str(conn.incr('article:'))
    voted = 'voted:' + article_id
    # the poster as the first voted:
    conn.sadd(voted, user)
    conn.expire(voted, ONE_WEEK_IN_SECONDS)

    now = time.time()
    article = 'article:' + article_id

    # add this article
    conn.hmset(article, {
        'title': title,
        'link': link,
        'poster': user,
        'time': now,
        'votes': 1,
    })

    # add this article to the sorted set
    conn.zadd('score:', article, now + VOTE_SCORE)
    conn.zadd('time:', article, now)
    return article_id

# 获取文章
ARTICLE_PER_PAGE = 25
def get_articles(conn, page, order='score:'):
    start = (page-1)*ARTICLE_PER_PAGE
    end = start + ARTICLE_PER_PAGE - 1

    # fetch the article ids
    ids = conn.zrevrange(order, start, end)
    # get article info from ids
    articles = []
    for id in ids:
        article_data = conn.hgetall(id)
        article_data['id'] = id
        article.append(article_data)
    return articles

# 对文章进行分组
def add_remove_groups(conn, article_id, to_add=[], to_remove=[]):
    article = 'article:' + article_id
    # add this article to the group
    for group in to_add:
        conn.sadd('group:' + group, article)
    # remove this article fromt the group
    for group in to_remove:
        conn.srem('group:' + group, article)

# 获取一组文章
def get_group_articles(conn, group, page, order='score:'):
    key = order + group
    # add this group's sort info
    if not conn.exists(key):
        conn.zinterstore(key,
            ['group:'+group, order],
            aggregate='max',
        )
        conn.expire(key, 60)
    # get a page of articles from the key
    return get_articles(conn, page, key)

转载于:https://my.oschina.net/u/3026036/blog/874237

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值