成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

文章投票網(wǎng)站的redis相關(guān)實(shí)現(xiàn)

沈儉 / 3324人閱讀

摘要:需求要構(gòu)建一個(gè)文章投票網(wǎng)站,文章需要在一天內(nèi)至少獲得張票,才能優(yōu)先顯示在當(dāng)天文章列表前列。根據(jù)評(píng)分或者發(fā)布時(shí)間對(duì)群組文章進(jìn)行排序和分頁(yè)文章添加的群組移除的群組群組有序集合名以上就是一個(gè)文章投票網(wǎng)站的相關(guān)實(shí)現(xiàn)。

需求:

要構(gòu)建一個(gè)文章投票網(wǎng)站,文章需要在一天內(nèi)至少獲得200張票,才能優(yōu)先顯示在當(dāng)天文章列表前列。

但是為了避免發(fā)布時(shí)間較久的文章由于累計(jì)的票數(shù)較多而一直停留在文章列表前列,我們需要有隨著時(shí)間流逝而不斷減分的評(píng)分機(jī)制。

于是具體的評(píng)分計(jì)算方法為:將文章得到的支持票數(shù)乘以一個(gè)常量432(由一天的秒數(shù)86400除以文章展示一天所需的支持票200得出),然后加上文章的發(fā)布時(shí)間,得出的結(jié)果就是文章的評(píng)分。

Redis設(shè)計(jì)

(1)對(duì)于網(wǎng)站里的每篇文章,需要使用一個(gè)散列來(lái)存儲(chǔ)文章的標(biāo)題、指向文章的網(wǎng)址、發(fā)布文章的用戶、文章的發(fā)布時(shí)間、文章得到的投票數(shù)量等信息。

為了方便網(wǎng)站根據(jù)文章發(fā)布的先后順序和文章的評(píng)分高低來(lái)展示文章,我們需要兩個(gè)有序集合來(lái)存儲(chǔ)文章:
(2)有序集合,成員為文章ID,分值為文章的發(fā)布時(shí)間。

(3)有序集合,成員為文章ID,分值為文章的評(píng)分。

(4)為了防止用戶對(duì)同一篇文章進(jìn)行多次投票,需要為每篇文章記錄一個(gè)已投票用戶名單。使用集合來(lái)存儲(chǔ)已投票的用戶ID。由于集合是不能存儲(chǔ)多個(gè)相同的元素的,所以不會(huì)出現(xiàn)同個(gè)用戶對(duì)同一篇文章多次投票的情況。

(5)文章支持群組功能,可以讓用戶只看見(jiàn)與特定話題相關(guān)的文章,比如“python”有關(guān)或者介紹“redis”的文章等,這時(shí),我們需要一個(gè)集合來(lái)記錄群組文章。例如 programming群組

為了節(jié)約內(nèi)存,當(dāng)一篇文章發(fā)布期滿一周之后,用戶將不能對(duì)它進(jìn)行投票,文章的評(píng)分將被固定下來(lái),而記錄文章已投票用戶名單的集合也會(huì)被刪除。

代碼設(shè)計(jì)

1.當(dāng)用戶要發(fā)布文章時(shí),
(1)通過(guò)一個(gè)計(jì)數(shù)器counter執(zhí)行INCR命令來(lái)創(chuàng)建一個(gè)新的文章ID。
(2)使用SADD將文章發(fā)布者ID添加到記錄文章已投票用戶名單的集合中,并用EXPIRE命令為這個(gè)集合設(shè)置一個(gè)過(guò)期時(shí)間,讓Redis在文章發(fā)布期滿一周后自動(dòng)刪除這個(gè)集合。
(3)使用HMSET命令來(lái)存儲(chǔ)文章的相關(guān)信息,并執(zhí)行兩ZADD命令,將文章的初始評(píng)分和發(fā)布時(shí)間分別添加到兩個(gè)相應(yīng)的有序集合中。

import time

# 截止時(shí)間,一周
ONE_WEEK_IN_SECONDS = 7 * (24 * 60 * 60)
# 計(jì)分常量
VOTE_SCORE = 432

"""
發(fā)布文章

@param {object}
@param {string} 用戶
@param {string} 文章title
@param

@return {string} 文章id
"""
def postArticle(conn, user, title, link):
    # 創(chuàng)建一個(gè)新的文章ID
    article_id = str(conn.incr("article:"))

    # 將文章發(fā)布者ID添加到記錄文章已投票用戶名單的集合中,并用EXPIRE為這個(gè)集合設(shè)置過(guò)期時(shí)間
    voted = "voted:" + article_id
    conn.sadd(voted, user)
    conn.expire(voted, ONE_WEEK_IN_SECONDS)
    
    now = time.time()
    
    # 用HMSET存儲(chǔ)文章的相關(guān)信息
    article = "article:" + article_id
    conn.hmset(article, {
        "title": title,
        "link": link,
        "poster": user,
        "time": now,
        "votes": 1
    })
    
    # 執(zhí)行兩個(gè)ZADD,將文章的初始評(píng)分與發(fā)布時(shí)間添加到兩個(gè)相應(yīng)的有序集合中
    conn.zadd("time:", article, now)
    conn.zadd("score:", article, now + VOTE_SCORE)
    
    return article_id

2.當(dāng)用戶嘗試對(duì)一篇文章進(jìn)行投票時(shí),
(1)用ZSCORE命令檢查記錄文章發(fā)布時(shí)間的有序集合(redis設(shè)計(jì)2),判斷文章的發(fā)布時(shí)間是否未超過(guò)一周。
(2)如果文章仍然處于可以投票的時(shí)間范疇,那么用SADD將用戶添加到記錄文章已投票用戶名單的集合(redis設(shè)計(jì)4)中。
(3)如果上一步操作成功,那么說(shuō)明用戶是第一次對(duì)這篇文章進(jìn)行投票,那么使用ZINCRBY命令為文章的評(píng)分增加432(ZINCRBY命令用于對(duì)有序集合成員的分值執(zhí)行自增操作);
并使用HINCRBY命令對(duì)散列記錄的文章投票數(shù)量進(jìn)行更新

"""
用戶投票功能

@param {object}
@param {string} 用戶
@param {string} 文章

"""
def voteArticle(conn, user, article):
    # 判斷文章是否超過(guò)了投票截止時(shí)間,如果超過(guò),則不允許投票
    outoff = time.time() - ONE_WEEK_IN_SECONDS
    if conn.zscore("time:", article) < outoff:
        return
    
    article_id = article.partition(":")[-1]
    # 將用戶添加到記錄已投票用戶名單的集合中
    if conn.sadd("voted:" + article_id, user):
        # 增加該文章的評(píng)分和投票數(shù)量
        conn.zincrby("score:", article, VOTE_SCORE)
        conn.hincrby(article, "votes", 1)

3.我們已經(jīng)實(shí)現(xiàn)了文章投票功能和文章發(fā)布功能,接下來(lái)就要考慮如何取出評(píng)分最高的文章以及如何取出最新發(fā)布的文章
(1)我們需要使用ZREVRANGE命令取出多個(gè)文章ID。(由于有序集合會(huì)根據(jù)成員的分值從小到大地排列元素,使用ZREVRANGE以分值從大到小的排序取出文章ID)
(2)對(duì)每個(gè)文章ID執(zhí)行一次HGETALL命令來(lái)取出文章的詳細(xì)信息。

這個(gè)方法既可以用于取出評(píng)分最高的文章,又可以用于取出最新發(fā)布的文章。

"""
取出評(píng)分最高的文章,或者最新發(fā)布的文章

@param {object}
@param {int}    頁(yè)碼
@param {string} 有序集合名稱,可以是score:,time:

@return array
"""
# 每頁(yè)的文章數(shù)
ARTICLES_PER_PAGE = 25

def getArticles(conn, page, order = "score:"):
    # 獲取指定頁(yè)碼文章的起始索引和結(jié)束索引
    start = (page - 1) * ARTICLES_PER_PAGE
    end   = start + ARTICLES_PER_PAGE - 1

    # 取出指定位置的文章id
    article_ids = conn.zrevrange(order, start, end)

    articles = []
    for id in article_ids:
        article_data = conn.hgetall(id)
        article_data["id"] = id

        articles.append(article_data)

    return articles

4. 對(duì)文章進(jìn)行分組,用戶可以只看自己感興趣的相關(guān)主題的文章。
群組功能主要有兩個(gè)部分:一是負(fù)責(zé)記錄文章屬于哪個(gè)群組,二是負(fù)責(zé)取出群組中的文章。

為了記錄各個(gè)群組都保存了哪些文章,需要為每個(gè)群組創(chuàng)建一個(gè)集合,并將所有同屬一個(gè)群組的文章ID都記錄到那個(gè)集合中。

"""
添加移除文章到指定的群組中

@param {object}
@param {int}   文章ID
@param {array} 添加的群組
@param {array} 移除的群組

"""
def addRemoveGroups(conn, article_id, to_add = [], to_remove = []):
    article = "article:" + article_id
    
    # 添加文章到群組中
    for group in to_add:
        conn.sadd("group:" + group, article)
    
    # 從群組中移除文章
    for group in to_remove:
        conn.srem("group:" + group, article)

由于我們還需要根據(jù)評(píng)分或者發(fā)布時(shí)間對(duì)群組文章進(jìn)行排序和分頁(yè),所以需要將同一個(gè)群組中的所有文章按照評(píng)分或者發(fā)布時(shí)間有序地存儲(chǔ)到一個(gè)有序集合中。
但我們已經(jīng)有所有文章根據(jù)評(píng)分和發(fā)布時(shí)間的有序集合,我們不需要再重新保存每個(gè)群組中相關(guān)有序集合,我們可以通過(guò)取出群組文章集合與相關(guān)有序集合的交集,就可以得到各個(gè)群組文章的評(píng)分和發(fā)布時(shí)間的有序集合。

Redis的ZINTERSTORE命令可以接受多個(gè)集合和多個(gè)有序集合作為輸入,找出所有同時(shí)存在于集合和有序集合的成員,并以幾種不同的方式來(lái)合并這些成員的分值(所有集合成員的分支都會(huì)視為1)。

對(duì)于文章投票網(wǎng)站來(lái)說(shuō),可以使用ZINTERSTORE命令選出相同成員中最大的那個(gè)分值來(lái)作為交集成員的分值:取決于所使用的排序選項(xiàng),這些分值既可以是文章的評(píng)分,也可以是文章的發(fā)布時(shí)間。

如下的示例圖,顯示了執(zhí)行ZINTERSTORE命令的過(guò)程:

對(duì)集合groups:programming和有序集合score:進(jìn)行交集計(jì)算得出了新的有序集合score:programming,它包含了所有同時(shí)存在于集合groups:programming和有序集合score:的成員。因?yàn)榧蟝roups:programming的所有成員分值都被視為1,而有序集合score:的所有成員分值都大于1,這次交集計(jì)算挑選出來(lái)的分值為相同成員中的最大分值,所以有序集合score:programming的成員分值實(shí)際上是由有序集合score:的成員的分值來(lái)決定的。

所以,我們的操作如下:
(1)通過(guò)群組文章集合和評(píng)分的有序集合或發(fā)布時(shí)間的有序集合執(zhí)行ZINTERSTORE命令,而得到相關(guān)的群組文章有序集合。

(2)如果群組文章很多,那么執(zhí)行ZINTERSTORE需要花費(fèi)較多的時(shí)間,為了盡量減少redis的工作量,我們將查詢出的有序集合進(jìn)行緩存處理,盡量減少ZINTERSTORE命令的執(zhí)行次數(shù)。

為了保持持續(xù)更新后我們能獲取到最新的群組文章有序集合,我們只將結(jié)果緩存60秒。

(3)使用上一步的getArticles函數(shù)來(lái)分頁(yè)并獲取群組文章。

"""
根據(jù)評(píng)分或者發(fā)布時(shí)間對(duì)群組文章進(jìn)行排序和分頁(yè)

@param {object}
@param {int}   文章ID
@param {array} 添加的群組
@param {array} 移除的群組

"""
def getGroupArticles(conn, group, page, order = "score:"):
    # 群組有序集合名
    key = order + group

    if not conn.exists(key):
        conn.zinterstore(key, ["group:" + group, order], aggregate = "max")
        conn.expire(key, 60)

    return getArticles(conn, page, key)

以上就是一個(gè)文章投票網(wǎng)站的相關(guān)redis實(shí)現(xiàn)。

測(cè)試代碼如下:

import unittest
class TestArticle(unittest.TestCase):
    """
    初始化redis連接
    """
    def setUp(self):
        import redis
        self.conn = redis.Redis(db=15)

    """
    刪除redis連接
    """
    def tearDown(self):
        del self.conn
        print
        print

    """
    測(cè)試文章的投票過(guò)程
    """
    def testArticleFunctionality(self):
        conn = self.conn
        import pprint

        # 發(fā)布文章
        article_id = str(postArticle(conn, "username", "A titile", "http://www.baidu.com"))
        print "我發(fā)布了一篇文章,id為:", article_id
        print
        self.assertTrue(article_id)
        
        article = "article:" + article_id
        # 顯示文章保存的散列格式
        print "文章保存的散列格式如下:"
        article_hash = conn.hgetall(article)
        print article_hash
        print
        self.assertTrue(article)

        # 為文章投票
        voteArticle(conn, "other_user", article)
        print "我們?yōu)樵撐恼峦镀?,目前該文章的票?shù):"
        votes = int(conn.hget(article, "votes"))
        print votes
        print
        self.assertTrue(votes > 1)

        print "當(dāng)前得分最高的文章是:"
        articles = getArticles(conn, 1)
        pprint.pprint(articles)
        print
        self.assertTrue(len(articles) >= 1)

        # 將文章推入到群組
        addRemoveGroups(conn, article_id, ["new-group"])
        print "我們將文章推到新的群組,其他文章包括:"
        articles = getGroupArticles(conn, "new-group", 1)
        pprint.pprint(articles)
        print
        self.assertTrue(len(articles) >= 1)

        測(cè)試結(jié)束,刪除所有的數(shù)據(jù)結(jié)構(gòu)
        to_del = (
            conn.keys("time:*") + conn.keys("voted:*") + conn.keys("score:*") + 
            conn.keys("articles:*") + conn.keys("group:*")
        )

        if to_del:
            conn.delete(*to_del)

if __name__ == "__main__":
    unittest.main()
代碼地址

https://github.com/NancyLin/r...

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/40758.html

相關(guān)文章

  • 使用Redis構(gòu)建文章投票網(wǎng)站(Java)

    摘要:文章投票網(wǎng)站的相關(guān)實(shí)現(xiàn)需求要構(gòu)建一個(gè)文章投票網(wǎng)站,文章需要在一天內(nèi)至少獲得張票,才能優(yōu)先顯示在當(dāng)天文章列表前列。文章發(fā)布期滿一周后,用戶不能在對(duì)它投票。此命令會(huì)覆蓋哈希表中已存在的域。 文章投票網(wǎng)站的redis相關(guān)Java實(shí)現(xiàn) 需求: 1、要構(gòu)建一個(gè)文章投票網(wǎng)站,文章需要在一天內(nèi)至少獲得200張票,才能優(yōu)先顯示在當(dāng)天文章列表前列。 2、但是為了避免發(fā)布時(shí)間較久的文章由于累計(jì)的票數(shù)較多...

    lpjustdoit 評(píng)論0 收藏0
  • Python--Redis實(shí)戰(zhàn):第一章:初識(shí)Redis:第三節(jié):你好Redis-文章投票試煉

    摘要:為了防止用戶對(duì)同一篇文章進(jìn)行多次投票,網(wǎng)站需要為每一篇文章記錄一個(gè)已投票用戶名單。上一篇文章實(shí)戰(zhàn)第一章初識(shí)第二節(jié)數(shù)據(jù)結(jié)構(gòu)簡(jiǎn)介下一篇文章實(shí)戰(zhàn)第二章使用構(gòu)建應(yīng)用第一節(jié)登錄和緩存 上一篇文章: Python--Redis實(shí)戰(zhàn):第一章:初識(shí)Redis:第二節(jié):Redis數(shù)據(jù)結(jié)構(gòu)簡(jiǎn)介下一篇文章:Python--Redis實(shí)戰(zhàn):第二章:使用Redis構(gòu)建Web應(yīng)用:第一節(jié):登錄和cookie緩存 ...

    Meils 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<