Dal:基于Mongodb和redis的Python持久库

该博客介绍了为解决Python项目中频繁的数据库和缓存交互问题而封装的dal库。该库自动处理缓存数据的判断与数据库加载,简化了代码,实现了对Redis和Mongodb的操作收敛。主要组件包括redis_proxy(Redis操作代理)、redis_pool(Redis连接池)、mongodb_pool(Mongodb连接池)和redis_list(多Redis配置支持)。重点讨论了nfind、RedisProxy.get和RedisProxy.generateKey等关键功能。
dal(Data Access Layer)的含义是数据访问层。

背景

在团队的开发中,我们主要是使用mongodb作为持久化db,redis作为缓存服务。两者组合交替使用,当访问缓存的数据不存在时,会访问db。然后把db的数据加载进缓存中。

在不断堆积的业务代码中,我发现了一个代码重复的问题。每次都是需要写那么一段判断缓存数据是否存在,不存在就从数据库加载的逻辑。基于这种情况,我封装了一套redis+mongodb的控制操作。由dal自己去判断缓存数据,并自动从数据库加载。持久层的代码量大大缩减。而且,操作行为都收敛到公开的接口,便于做统一的升级和各种统计。

结构

dal类图

redis_proxy:用于代理redis的各种操作
redis_pool:python版redis的连接池
mongodb_pool:pymongodb的连接池
redis_list:支持配置多个redis

代码分析

nfind

@ctime(NAME)
    """
    table:mongodb的表名
    prefix:缓存前缀
    query:查询条件,dict类型
    cache:是否使用缓存
    cache_time:缓存时间
    sort:排序条件
    criteria:查询的字段
    limit:结果数量
    cache_kw:关联的缓存key
    pack:缓存value是否使用msgpack
    """
    def nfind(self, table, prefix="", query={}, cache=True, cache_time=43200, sort=None, criteria=None, limit=None, cache_kw=None, pack=True):
        result = None
        try:
            """查询缓存"""
            if cache:
                result = self.redis_proxy.read_by_cache_type(CACHETYPE.string ,table, prefix, query, cache_time, sort=sort, limit=limit, criteria=criteria, pack=pack)
                if result is not None:
                    return result

            """从mongodb获取查询的cursor"""
            if criteria:
                cursor = self.get_mongodb()[table].find(query,criteria)
            else:
                cursor = self.get_mongodb()[table].find(query)

            """如果有排序条件,进行排序操作"""
            if sort:
                if isinstance(sort, tuple):
                    cursor = cursor.sort(*sort)
                else:
                    cursor = cursor.sort(sort)

            """限定结果集数量"""
            if limit and limit > 0:
                cursor = cursor.limit(limit)

            """把查询结果转化为list类型"""
            result = list(cursor)

            """把主键_id转为str类型"""
            for r in result:
                if "_id" in r:
                    r["_id"] = str(r.get("_id"))

            """操作缓存"""
            if cache:
                if self.debug:
                    self.logger.debug("[Dal.nfind]write_by_cache_type table=%s, prefix=%s, query=%s" %(table, prefix, query))

                self.redis_proxy.write_by_cache_type(CACHETYPE.string, table, prefix=prefix, value=result, query=query,criteria=criteria,cache_time=cache_time,sort=sort,limit=limit,cache_kw=cache_kw, pack=pack)

            return result
        except Exception, e:
            self.logger.error("[Dal.nfind]error: %s, bt: %s" %(e, traceback.format_exc()))
            return None
        finally:
            if self.debug:
                self.logger.debug("[Dal.find]finally table=%s, prefix=%s, query=%s, cache=%s, cache_time=%s, criteria=%s" %(table, prefix, query, cache, cache_time, criteria))

RedisProxy.get

代理缓存的get操作

@ctime(REDIS_STAT_NAME)
    def get(self, table, prefix="", query={}, cache_time=0, sort=None, limit=None, criteria=None, pack=True):
        """生成通用的key"""
        key = self.generateKey(table, prefix, query, sort=sort, limit=limit, criteria=criteria, pack=pack)
        status = None
        result = None
        try:
            status = self.dal.get_redis().exists(key)
            if status:
                result = self.dal.get_redis().get(key)
                if pack:
                    """unpackb二进制数据"""
                    return msgpack.unpackb(result, use_list = True)
                elif result:
                    return json.loads(result, "UTF-8")
            return None
        except Exception:
            self.dal.logger.error("[RedisProxy.get]error, result=%s, %s" %(result, traceback.format_exc()))
            return None
        finally:
            if self.dal.debug:
                self.dal.logger.debug("[RedisProxy.get]key=%s, status=%s" %(key, status))

RedisProxy.generateKey

生成redis的key
通过key是以tablecache_table开头,table是mongodb的集合表名
prefix:缓存前缀
query(查询条件),sort(排序条件),criteria(查询字段)
limit(限定值)
pack(使用msgpack处理)

def generateKey(self, table, prefix="", query={}, sort=None, limit=None, name="tablecache", criteria=None, pack=True):
        key = "%s_%s" %(name,table)

        if prefix:
            key = "%s_%s" %(key, prefix) 

        if query:
            key = key + "_" + "_".join(sorted(["%s_%s" %(key,value) for key,value in query.iteritems()], key=lambda a:a))

        if sort and isinstance(sort, tuple):
            key = key + "_$sort_" + "_".join([ "%s" %val for val in sort])

        if criteria:
            key = key + "_$criteria_" + "_".join(sorted(["%s_%s" %(key,value) for key,value in criteria.iteritems()], key=lambda a:a))

        if limit:
            key = key + "_$limit_%s" %(limit)

        if pack:
            key = key +"_$pack_1"
        else:
            key = key +"_$pack_0"

        return key
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值