Flask中用Redis存储统计数据

Flask使用Redis统计数据(持久化存储)

  1. Redis做持久化存储

    用户发表文章的统计:
      news_article_basic
      sql: select count(*) from news_article_basic where user_id=1;
      user_basic: article_count, fans_count
     	sql: select article_count from user_basic where user_id=1;
      user_tongji: user_id, fans_count
      缓存
    	原来统计数据的设计:在user_basic表中,添加字段进行统计。
    	存在的问题:
    		1.在高并发的情况下,在统一时刻触发统计的数量是巨大的。
    		2.为了数据的准确性,又必须加锁。所以对数据库的性能影响是巨大的。
    
    在redis中,每个命令都是原子性的,既能提供高性能的访问,也能保证数据修改的完整性。
    
    所以在项目中:
    	1.统计数据最终的选择方案是使用redis进行缓存。
    	2.redis持久保存层选择的是主从redis,因为数据要永久存储,没有对主从设置淘汰策略。
    	3.redis集群是用做缓存层,设置了淘汰策略。
    
  2. 用户发布文章数量统计分析

    用户发布文章redis数据库设计思路分析:
    所有用户发布文章数量全部放在一个key中.
    	key: count:user:arts
    	value: 值选择zset类型
    	member: 选择user_id
    	score:  选择用户发布文章的数量
    
    需求分析:
    	1.获取用户发布文章数量
    		发布文章数量 = zscore(key, user_id)
    	2.发布文章,用户发布文章数量加1.
    		zincrby(key, user_id, 1)
    		
    错误的操作:
    	count = zscore(key, user_id)
    	# 此时有可能出现并发
    	count += 1
    	zadd(key, count, user_id)
    	这样会存在并发改的情况
    
    正确的操作:
    	zincrby(key, user_id, 1)
    
    备注:
    	执行zincrby命令,是原子性的,不会出现并发改的情况,只有要么成功要么失败。
    
  3. 代码实现(存储类封装)除了文章数量还有文章阅读,文章收藏数量等待,代码都是相似的只有key值不同,所以写了一个父类,子类继承即可

    class CountStorageBase(object):
        """
        数据存储父类
        """
        key = ''
    
        @classmethod
        def get(cls, id_value):
            """
            获取
            """
            try:
                count = current_app.redis_master.zscore(cls.key, id_value)
            except ConnectionError as e:
                current_app.logger.error(e)
                try:
                    count = current_app.redis_slave.zscore(cls.key, id_value)
                except RedisError as e:
                    current_app.logger.error(e)
                    count = 0
    
            count = 0 if count is None else int(count)
    
            return count
    
        @classmethod
        def incr(cls, id_value, increment=1):
            """
            增加
            """
            try:
                current_app.redis_master.zincrby(cls.key, id_value, increment)
            except RedisError as e:
                current_app.logge.error(e)
    
    
        @classmethod
        def reset(cls, redis_client, *items):
            """
            重置数值,用于定时任务纠偏使用
            """
            pl = redis_client.pipeline()
            pl.delete(cls.key)
            pl.zadd(cls.key, *items)
            pl.execute()
    
    
    class UserArticlesCountStorage(CountStorageBase):
        """
        用户文章数量
        """
        key = 'count:user:arts'
        # 后续用来修正统计数据使用
        @classmethod
        def db_query(cls):
            ret = db.session.query(Article.user_id, func.count(Article.id)) \
                .filter(Article.status == Article.STATUS.APPROVED).group_by(Article.user_id).all()
            return ret
    

版权声明:本文为weixin_45439324原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。