使用Python装饰器构造一个LRU本地缓存

本地缓存指的是在代码运行中动态分配的内存,一般存在于当前进程地址空间的堆内存中,如果不人为的显式释放,将会一直存在进程中。本地缓存和 Memcached 或 Redis 这样的远程缓存区别是,本地缓存有更高的执行效率,没有网络传输的消耗,缺点是没有自动过期的机制,存放的数据量也有限。

本地缓存可以显著提高代码的执行效率,比如,看下面这样一段代码:

def get_user_info(user_id):
    """查询用户信息"""
    sql = "select * from user_info where user_id={user_id}".format(user_id=user_id)
    user_info = db.query(sql)
    return user_info

如果这个函数被频繁调用(比如放在循环中),每次都需要去查询数据库,效率必然很低,那么可以使用本地缓存来优化:

def get_user_info(user_id, cache={}):
    """查询用户信息"""
    if user_id in cache:
        # 先查询本地缓存
        return cache[user_id]
    sql = "select * from user_info where user_id={user_id}".format(user_id=user_id)
    user_info = db.query(sql)
    cache[user_id] = user_info
    return user_info

这样,已经查询过一次的用户信息将会存在于本地缓存 cache 中,而不用每次去数据库查询。不过,这样的本地缓存有两个缺点:

为了解决这两个问题,我们来构造一个专门用于本地缓存的 Python 装饰器,提供两个功能:

具体实现看如下代码:

缓存工具类 mycache.py

# mycache.py
import time
from collections import OrderedDict


class LRUCache:
    def __init__(self, capacity=1000):
        self.capacity = capacity
        self.cache = OrderedDict()

    def set(self, key, value):
        if len(self.cache) >= self.capacity:
            self.cache.popitem(last=False)
        if key in self.cache:
            self.cache.pop(key)
        self.cache[key] = value

    def get(self, key):
        if key not in self.cache:
            return None
        value = self.cache[key]
        self.cache.pop(key)
        self.cache[key] = value
        return value

    def pop(self, key):
        self.cache.pop(key)
        
        
class Cache:
    native_cache = LRUCache()
    
    @staticmethod
    def getNativeCache(key, expire_ts=300):
        info = Cache.native_cache.get(key)
        if info is not None:
            ts = info["ts"]
            diff_ts = int(time.time()) - ts
            if diff_ts > expire_ts:
                # 过期失效
                Cache.native_cache.pop(key)
                return None
            return info["value"]
        return None

    @staticmethod
    def setNativeCache(key, value):
        info = dict(value=value, ts=int(time.time()))
        Cache.native_cache.set(key, info)

在公共模块 common.py 中写一个装饰器

# common.py
import mycache

def native_cache(business_type, expire_ts=300):
    """
    装饰器本地缓存,目前只支持*args参数
    """
    def decorator(func):
        def wrapper(*args):
            key = business_type + "_" + "_".join(map(str, args))
            value = mycache.Cache.getNativeCache(key, expire_ts)
            if value is None:
                value = func(*args)
                mycache.Cache.setNativeCache(key, value)
            return value
        return wrapper
    return decorator

构造完成,用法如下:

import common

@common.native_cache("user_info", expire_ts=300)
def get_user_info(user_id):
    """查询用户信息"""
    sql = "select * from user_info where user_id={user_id}".format(user_id=user_id)
    user_info = db.query(sql)
    return user_info

 


关注微信公众账号「曹当家的」,订阅最新文章推送

Table of Contents