美文网首页
redis知识回顾(3)

redis知识回顾(3)

作者: 指向远方的灯塔 | 来源:发表于2019-04-25 18:21 被阅读0次
    3.论坛构建示例



    # encoding: utf-8
    
    from id_generator import IdGenerator
    
    def make_user_key(uid):
        return 'bbs::user::' + str(uid)
    
    def make_email_to_uid_key():
        return 'bbs::email_to_uid'
    
    class User:
    
        """
        用户相关操作。
        """
    
        def __init__(self, client):
            self.client = client
    
        def create(self, name, email, password):
            """
            创建一个新的用户,创建前需要确保输入的用户名和邮箱未被使用。
            """
            # 储存用户信息
            new_id = IdGenerator(client, "bbs::user_id").gen()
            key = make_user_key(new_id)
            self.client.hmset(key, {
                                    'id': new_id,
                                    'name': name,
                                    'email': email,
                                    'password': password
                                    })
            # 关联用户 email 与 ID
            self.client.hset(make_email_to_uid_key(), email, new_id)
            # 返回用户 ID 作为函数的值
            return new_id
    
        def get_by_id(self, uid):
            """
            根据给定的用户 id ,获取用户的详细信息。
            """
            key = make_user_key(uid)
            return self.client.hgetall(key)
    
        def try_login(self, email, password):
            """
            尝试使用指定的邮箱和密码进行登录,成功时返回用户信息,失败时返回 None 。
            """
            # 根据输入的邮箱,获取用户 ID
            uid = self.client.hget(make_email_to_uid_key(), email)
            # 如果找不到与邮箱对应的用户 ID ,那么说明这个邮箱未注册过账号
            if uid is None:
                return None
    
            # 根据用户 ID ,获取用户信息,并进行密码对比
            # 邮箱不用对比,因为用户信息就是根据邮箱来查找的
            user_info = self.get_by_id(uid)
            if user_info['password'] == password:
                return user_info
    
    





    # encoding: utf-8
    
    from time import time as current_time
    
    def make_category_key(name):
        return 'bbs::category::' + name
    
    class Category:
    
        """
        创建一个分类列表来记录所有属于某个分类的文章。
        """
    
        def __init__(self, client, name):
            self.client = client
            self.category_zset = make_category_key(name)
    
        def include_topic(self, topic_id):
            """
            将指定的文章添加到当前分类里面。
            """
            # 文章按照添加的时间来排序。
            self.client.zadd(self.category_zset, topic_id, current_time())
    
        def is_included(self, topic_id):
            """
            检查指定的文章是否为当前分类的文章。
            """
            # 当 topic_id 作为元素存在于 ZSET 时,它的分值不为 None
            return self.client.zscore(self.category_zset, topic_id) is not None
    
        def count_topic(self):
            """
            返回分类目前包含的文章数量。
            """
            return self.client.zcard(self.category_zset)
    
        def paging(self, n, count):
            """
            按时间从新到旧的顺序,以 count 篇文章为一页,返回当前分类第 n 页上的文章。
            """
            start_index = (n-1)*count
            end_index = n*count-1
            return self.client.zrevrange(self.category_zset, start_index, end_index)
    
    




    # encoding: utf-8
    
    from category import Category
    
    from time import time as current_time
    
    def make_tab_member_key(name):
        return 'bbs::tab::' + name + '::member'
    
    def make_tab_list_key(name):
        return 'bbs::tab::' + name + 'topic_list'
    
    # 使用 Tab 来记录所有分类属下文章的另一种做法是,直接对各个分类的文章列表进行并集计算:
    # 这种实现可以避免每个 Tab 单独维护一个文章列表,缺点是并集计算比较耗时,
    # 所以为了高效起见,这里还是使用单独维护一个文章列表的做法。
    class Tab:
    
        """
        一个标签可以包含任意多个分类,并按照发布时间先后,记录被包含分类属下的文章。
        """
    
        def __init__(self, client, name):
            self.client = client
            # 储存标签属下分类的集合
            self.tab_member_set = make_tab_member_key(name)
            # 储存标属下分配的文章的有序集合
            self.tab_topic_list = make_tab_list_key(name)
    
        def add_member(self, category_name):
            """
            将指定分类添加到当前标签中。
            """
            self.client.sadd(self.tab_member_set, category_name)
    
        def is_member(self, category_name):
            """
            检查指定分类是否属于当前标签。
            """
            return self.client.sismember(self.tab_member_set, category_name)
    
        def get_all_member(self):
            """
            返回标签包含的所有分类。
            """
            return self.client.smembers(self.tab_member_set)
    
        def count_member(self):
            """
            返回标签包含的分类数量。
            """
            return self.client.scard(self.tab_member_set)
    
        def include_topic(self, category_name, topic_id):
            """
            将属于 category 分类的文章添加到当前标签的时间线里面。
            """
            # 先检查文章是否属于 category 分类
            # 然后检查 category 分类是否属于当前标签
            # 只有两个条件都通过时,才将文章添加到当前时间线
            category = Category(self.client, category_name)
            if self.is_member(category_name) and category.is_included(topic_id):
                self.client.zadd(self.tab_topic_list, topic_id, current_time())
                
    
        def paging(self, n, count):
            """
            按时间从新到旧的顺序,以 count 篇文章为一页,返回当前标签第 n 页上的文章。
            """
            start_index = (n-1)*count
            end_index = n*count-1
            return self.client.zrevrange(self.tab_topic_list, start_index, end_index)
    
        def count_topic(self):
            """
            返回标签目前包含的文章数量。
            """
            return self.client.zcard(self.tab_topic_list)
    


    # encoding: utf-8
    
    from redis import WatchError
    
    def make_vote_up_key(topic_id):
        return 'bbs::topic::' + str(topic_id) + '::vote_up'
    
    def make_vote_down_key(topic_id):
        return 'bbs::topic::' + str(topic_id) + '::vote_down'
    
    class TopicVote:
    
        """
        对帖子进行投票,可以投支持票也可以投反对票。
        """
    
        def __init__(self, client, topic_id):
            """
            设置要被投票的帖子。
            """
            self.client = client
            self.vote_up_set = make_vote_up_key(topic_id)
            self.vote_down_set = make_vote_down_key(topic_id)
    
        def is_voted(self, user_id):
            """
            检查用户是否已经对这个帖子投过票。
            """
            return self.client.sismember(self.vote_up_set, user_id) or \
                   self.client.sismember(self.vote_down_set, user_id)
    
        def vote_up(self, user_id):
            """
            用户对这个帖子投支持票。
            """
            if not self.is_voted(user_id):
                self.client.sadd(self.vote_up_set, user_id)
    
        def vote_down(self, user_id):
            """
            用户对这个帖子投反对票。
            """
            if not self.is_voted(user_id):
                self.client.sadd(self.vote_down_set, user_id)
    
        def undo(self, user_id):
            """
            撤销用户的投票。
            """
            self.client.srem(self.vote_up_set, user_id)
            self.client.srem(self.vote_down_set, user_id)
    
        def vote_up_count(self):
            """
            返回帖子目前获得的支持票数量。
            """
            return self.client.scard(self.vote_up_set)
    
        def vote_down_count(self):
            """
            返回帖子目前获得的反对票数量。
            """
            return self.client.scard(self.vote_down_set)
    
    # encoding: utf-8
    
    from datetime import datetime, timedelta
    
    def today_remaind_seconds():
        """
        返回当天剩余的秒数。
        """
        now = datetime.now()
        tomorrow = now + timedelta(days=1) - timedelta(hours=now.hour, minutes=now.minute,seconds=now.second)
        return (tomorrow-now).seconds
    
    DAY_RANK_KEY = 'bbs::reply_day_rank'
    
    class ReplyDayRank:
    
        """
        按照帖子获得的回复数量,对帖子进行排行。
        排行榜每天更新一次。
        """
    
        def __init__(self, client):
            self.key = DAY_RANK_KEY
            self.client = client
    
        def incr_reply_count(self, topic_id):
            """
            对指定帖子在当天的回复数量增一,每次帖子有新回复出现时调用。
            """
            # 为计数器增一
            self.client.zincrby(self.key, topic_id, 1)
            # 如果键未设置生存时间或过期时间,那么将它的生存时间设置为当天剩下的秒数
            if self.client.ttl(self.key) is None:
                self.client.expire(self.key, today_remaind_seconds())
    
        def get_top(self, n, show_score=False):
            """
            返回当天回复数量排在前 n 位的帖子。
            """
            return self.client.zrevrange(self.key, 0, n-1, withscores=show_score)
    




    reddit_algorithm.py

    # Reddit 所使用的排序算法
    # 详细信息请参考: http://www.ruanyifeng.com/blog/2012/03/ranking_algorithm_reddit.html
    
    from datetime import datetime, timedelta
    from math import log
    
    epoch = datetime(1970, 1, 1)
    
    def epoch_seconds(date):
        """Returns the number of seconds from the epoch to date."""
        td = date - epoch
        return td.days * 86400 + td.seconds + (float(td.microseconds) / 1000000)
    
    def score(ups, downs):
        return ups - downs
    
    def hot(ups, downs, date):
        """The hot formula. Should match the equivalent function in postgres."""
        s = score(ups, downs)
        order = log(max(abs(s), 1), 10)
        sign = 1 if s > 0 else -1 if s < 0 else 0
        seconds = epoch_seconds(date) - 1134028003
        return round(order + sign * seconds / 45000, 7)
    
    # encoding: utf-8
    
    import reddit_algorithm
    
    from datetime import datetime
    
    def make_recommand_list_key():
        return 'bbs::recommand::list'
    
    class TopicRecommand:
    
        """
        根据帖子获得的投票数量以及帖子的发布时间,对帖子进行评分,
        并根据评分对帖子进行排序。
        """
    
        def __init__(self, client):
            self.client = client
            self.recommand_list = make_recommand_list_key()
    
        def update_rank(self, topic_id, up_vote, down_vote, post_time):
            """
            根据文章的投票数和发布时间,更新文章在推荐列表中的排位。
            """
            # 因为算法只接受 datetime 格式的发布时间,而我们记录的文章发布时间是 UNIX 时间戳
            # 所以这里需要将 UNIX 时间戳转换为 Python 的 datetime 时间
            post_time_in_datetime_format = datetime.fromtimestamp(post_time)
            # 使用推荐算法计算出帖子的分值
            score = reddit_algorithm.hot(up_vote, down_vote, post_time_in_datetime_format)
            # 为帖子设置新的分值,并更新帖子在推荐列表中的排位
            self.client.zadd(self.recommand_list, topic_id, score)
    
        def paging(self, n, count):
            """
            按时间从新到旧的顺序,以 count 篇文章为一页,返回推荐列表第 n 页上的文章。
            """
            start_index = (n-1)*count
            end_index = n*count-1
            return self.client.zrevrange(self.recommand_list, start_index, end_index)
    
    4.博客示例






    id_generator.py

    # coding: utf-8
    
    class IdGenerator:
    
        """
        使用字符串键实现的自增唯一 ID 生成器。
        """
    
        def __init__(self, client, key):
            """
            设置储存 ID 生成器当前值的键。
            """
            self.client = client
            self.key = key
    
        def init(self, n):
            """
            初始化生成器的值,需要在系统正式运行之前调用,用于保留少于等于 n 的 ID 号码。
            """
            # 如果键 key 已经有值,那么说明 gen() 已经执行过
            # 为了防止产生重复的 ID ,程序不能执行这个 init() 操作
            if self.client.get(self.key) is not None:
                raise Exception
            self.client.set(self.key, n)
    
        def gen(self):
            """
            生成一个新的唯一 ID 。
            """
            new_id = self.client.incr(self.key)
            return int(new_id)
    

    unique_set.py

    # encoding: utf-8
    
    class UniqueSet:
    
        def __init__(self, client, key):    
            self.client = client
            self.key = key
    
        def add(self, element):
            self.client.sadd(self.key, element)
    
        def is_include(self, element):
            return self.client.sismember(self.key, element)
    

    user.py

    # encoding: utf-8
    
    def make_user_key(account):
        return 'blog::user::' + account
    
    class User:
    
        """
        用户账号操作。
        """
    
        def __init__(self, client):
            self.client = client
    
        def create(self, account, password, nickname):
            """
            创建新用户,创建钱需要确保给定的 account 未被占用。
            """
            key = make_user_key(account)
            info = {
                    'account': account,
                    'nickname': nickname,
                    'password': password
                   }
            self.client.hmset(key, info)
    
        def get_by_account(self, account):
            key = make_user_key(account)
            return self.client.hgetall(key)
    
        def try_login(self, account, password):
            user_info = self.get_by_account(account)
            if (user_info is not None) and (user_info['password'] == password):
                return user_info
    

    # encoding: utf-8
    
    from time import time as current_time
    from id_generator import IdGenerator
    
    ID_GENERATOR_KEY = 'blog::post_ids'
    
    def make_post_key(post_id):
        return 'blog::post::' + str(post_id)
    
    class Post:
    
        """
        文章相关操作。
        """
    
        def __init__(self, client):
            self.client = client
    
        def create(self, title, content, category, author_account):
            """
            创建一篇新文章。
            """
            post_id = IdGenerator(client, ID_GENERATOR_KEY).gen()
            post_hash = make_post_key(post_id)
            info = {
                    'id': post_id,
                    'time': current_time(),
                    'title': title,
                    'content': content,
                    'category': category,
                    'author_account': author_account
                   }
            self.client.hmset(post_hash, info)
            return post_id
    
        def get_by_id(self, post_id):
            post_hash = make_post_key(post_id)
            return self.client.hgetall(post_hash)
    

    def __init__(self, client):
            self.client = client
            self.post_list = make_index_key()
    
        def push(self, post_id):
            """
            将文章推入到列表里面。
            """
            self.client.lpush(self.post_list, post_id)
    
        def count_post(self):
            """
            返回列表目前已有的文章数量。
            """
            return self.client.llen(self.post_list)
    
        def paging(self, n, count):
            """
            对文章进行分页。
            """
            start_index = (n-1)*count
            end_index = n*count-1
            return self.client.lrange(self.post_list, start_index, end_index)
    
    



    # encoding: utf-8
    
    def make_category_key(name):
        return 'blog::category::' + name
    
    class Category:
    
        """
        创建一个分类列表来储存所有属于某个分类的文章。
        """
    
        def __init__(self, client, name):
            self.client = client
            self.post_list = make_category_key(name)
    
        def include_post(self, post_id):
            """
            将指定的文章添加到当前分类里面。
            """
            self.client.lpush(self.post_list, post_id)
    
        def count_post(self):
            return self.client.llen(self.post_list)
    
        def paging(self, n, count):
            """
            按时间从新到旧的顺序,以 count 篇文章为一页,返回当前分类第 n 页上的文章。
            """
            start_index = (n-1)*count
            end_index = n*count-1
            return self.client.lrange(self.post_list, start_index, end_index)
    
    image.png

    comment.py

    # encoding: utf-8
    
    from time import time
    from id_generator import IdGenerator
    
    ID_GENERATOR_KEY = 'blog::comment_ids'
    
    def make_comment_key(comment_id):
        return 'blog::comment::' + str(comment_id)
    
    class Comment:
    
        """
        帖子评论相关操作。
        """
    
        def __init__(self, client):
            self.client = client
    
        def create(self, author_id, content):
            """
            创建一个新的评论。
            """
            comment_id = IdGenerator(client, ID_GENERATOR_KEY).gen()
            comment_hash = make_comment_key(comment_id)
            info = {
                    'id': comment_id,
                    'author_id': author_id,
                    'content': content,
                    'time': time()
                   } 
            self.client.hmset(comment_hash, info)
            return comment_id
    
        def get_by_id(self, comment_id):
            """
            根据评论 id ,查找并返回评论的详细信息。
            """
            comment_hash = make_comment_key(comment_id)
            return self.client.hgetall(comment_hash)
    

    comment_list.py

    # encoding: utf-8
    
    def make_comment_list_key(post_id):
        return 'blog::post::' + str(post_id) + '::comments'
    
    class CommentList:
    
        """
        创建一个列表来记录某一帖子下的所有评论(的 ID)。
        """
    
        def __init__(self, client, post_id):
            """
            设置被评论的帖子的 ID 。
            """
            self.client = client
            self.post_id = post_id
            self.comment_list = make_comment_list_key(post_id)
    
        def push(self, comment_id):
            """
            将评论推入到评论列表里面。
            """
            self.client.lpush(self.comment_list, comment_id)
    
        def count(self):
            """
            返回帖子目前已有的评论数量。
            """
            return self.client.llen(self.comment_list)
    
        def paging(self, number, count):
            """
            以分页形式返回帖子的评论。
            """
            start_index = (number-1)*count
            end_index = number*count-1
            return self.client.lrange(self.comment_list, start_index, end_index)
    

    服务器配置

    通过调整服务器来适应你的需求











    RDB持久化













    AOF持久化












    服务器管理









    现成的redis管理工具















    redis多机功能导论

    从单台redis服务器过度到多台redis服务器














    分片














    image.png
    image.png



    redis集群




    image.png
    image.png

    image.png

    image.png









    redis的实现原理















    image.png

    相关文章

      网友评论

          本文标题:redis知识回顾(3)

          本文链接:https://www.haomeiwen.com/subject/jzoxgqtx.html