Dal:基於Mongodb和redis的Python持久庫

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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章