Python 使用缓存功能进行接口性能调优

背景

之前提过我们给组内开发了一个需求进度看板,数据源一周更新一次。由于数据量是两年的需求总量,接口查询比较慢,耗时10s左右。然后开发提供了一个思路,就是获取第一次请求的数据,存到缓存,后续再从缓存取数据,大大提高接口请求耗时。

python内置缓存模块

functools 模块的 lur_cache装饰器可以提供缓存功能,从而提高程序执行效率。

参数:

  • maxsize: 如果为None,则无限制,设置为2的幂时,性能最佳

  • typed: 如果typed为True,则不同参数类型的调用将分别缓存,例如f(3)和f(3.0)

示例代码:

from functools import lru_cache

# 这种缓存适合服务端
@lru_cache(maxsize=20)
def fib(n):
    print("此函数执行了")
    if n < 2:
        return n
    return fib(n - 1) + fib(n - 2)


# 执行了函数,返回了结果 2
print(fib(3))
# 没有执行 fib函数,读取了缓存中的结果
print(fib(3))

运行结果:

释义:

使用lru_cache装饰器后,每次递归若有相同的参数调用,直接从缓存中取值,则fib(1)的调用只有一次,效率明显提升

使用redis进行缓存

上述方法可以实现本地的数据缓存,但是当我把代码发到服务器上,发现有不同集群的机器,里面的缓存不互通,不能满足我的需求,故采取redis方法设置缓存。

查了资料,使用 redis 自定义装饰器实现缓存比较适合我。下面是一个示例:

import redis
import pickle
import functools

def cache(redis_host, redis_port, redis_db, cache_time=60):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            r = redis.Redis(host=redis_host, port=redis_port, db=redis_db)
            key = pickle.dumps((func.__name__, args, kwargs))
            result = r.get(key)
            if result:
                return pickle.loads(result)
            else:
                result = func(*args, **kwargs)
                r.setex(key, cache_time, pickle.dumps(result))
                return result
        return wrapper

    
@cache(redis_host='localhost', redis_port=6379, redis_db=0, cache_time=60)
def expensive_operation(x, y):
    # some expensive operation
    return result

这个装饰器函数接受4个参数:

redis_host:Redis服务器的地址

redis_port:Redis服务器的端口号

redis_db:Redis数据库的编号

cache_time:缓存的有效期时间,默认为60秒

装饰器内部定义了一个嵌套函数wrapper,这个函数负责实现缓存的逻辑。首先,使用pickle模块将函数名、参数和关键字参数打包成一个键值,然后使用Redis的get方法获取缓存中的结果,如果结果存在,则直接返回缓存结果;否则,调用被装饰的函数获取结果,再将结果保存到Redis缓存中。

装饰器会将expensive_operation的结果缓存起来,下次再调用expensive_operation函数时,如果参数相同,则直接返回缓存的结果,不用再次执行函数。这样可以大大提高程序的性能

Last updated