LRU算法原理
LRU (Least Recently Used,最久未使用) 算法是一种缓存淘汰策略。其根据数据的历史访问记录来进行淘汰,核心思想是,“如果数据最近被访问过,那么将来被访问的几率也更高”。该算法最初为操作系统中一种内存管理的页面置换算法,主要用于找出内存中较久时间没有使用的内存块,将其移出内存从而为新数据提供空间。
Python中的LRU
Python 中同样存在缓存机制,即内置模块functool 中的 lru_cache 装饰器,可以直接将函数或类方法的结果缓存住,后续调用则直接返回缓存的结果。用以节约高开销或I/O函数的调用时间。
@functools.lru_cache(maxsize=None, typed=False)
参数 | 默认值 | 说明 |
maxsize | 128 | 如果 maxsize 设为 None ,LRU 特性将被禁用且缓存可无限增长。设置为 2 的幂时,性能最佳。 |
typed | False | 如果 typed 被设置为 true ,不同类型的函数参数将被分别缓存。 比如区分浮点数与整型。 |
注意事项:
1、由于使用字典来缓存结果,因此传给该函数的位置和关键字参数必须为 hashable。
2、不同的参数模式可能会被视为具有单独缓存项的不同调用。 (缓存按照参数作为键)
案例:利用@lru_cache装饰器计算斐波那契数
这里我们先写一个统计时间的装饰器,后面会用到。
import time
def show_time(func):
def inner(*args,**kwargs):
start_time = time.perf_counter()
res = func(*args,**kwargs)
end_time = time.perf_counter()
print(f"The execution time: {end_time - start_time:.8f} seconds")
return res
return inner
计算fibonacci(30)的时候很耗时,很多前面的fibonacci数在递归过程中会重复计算多次,耗时较长。
def fibonacci(num):
return num if num < 2 else fibonacci(num - 1) + fibonacci(num - 2)
@show_time
def fib(n):
return fibonacci(n)
print(fib(30))
# 输出:
# The execution time: 1.57796764 seconds
# 832040
使用@lru_cache装饰器后,发现运行速度快了很多,因为使用了lru_cache则把之前执行的函数结果已经缓存了起来,就不需要再次执行了。
@lru_cache(maxsize=None)
def fibonacci(num):
return num if num < 2 else fibonacci(num - 1) + fibonacci(num - 2)
@show_time
def fib(n):
return fibonacci(n)
print(fib(30))
# 输出:
# The execution time: 0.00015267 seconds
# 832040
通过上面的例子可以发现,当我们使用递归求斐波拉契数列 (斐波那契数列指的是这样一个数列:0,1,1,2,3,5,8,它从第3项开始,每一项都等于前两项之和) 的时候,缓存对性能的提升就尤其明显。
实际应用:如编写接口时缓存一些变动不大的数据如配置信息
比如:缓存从数据库查询的用户信息,下次再调用这个接口时将直接返回用户信息列表而不需要重新执行一遍数据库查询逻辑,可以有效较少IO次数,加快接口反应速度。
@api.route("/user/info", methods=["POST"])
@login_required
@functools.lru_cache()
def add_user():
user = UserInfo(name="李四")
db.session.add(user)
db.session.commit()
# 清除get_userinfo_list中的缓存
get_userinfo_list = current_app.view_functions["api.get_machine_list"]
cache_info = get_userinfo_list.__wrapped__.cache_info()
# cache_info 具名元组,包含命中次数 hits,未命中次数 misses ,最大缓存数量 maxsize 和 当前缓存大小 currsize
# 如果缓存数量大于0则清除缓存
if cache_info[3] > 0:
get_userinfo_list.__wrapped__.cache_clear()
return jsonify("新增用户成功")