一、问题描述
后台项目中,经常会遇到复杂的业务查询(关联多张数据表),或者查询大量数据的分析统计结果,这样的请求都是耗时较长的,如果大量用户频繁出现这种操作,会给服务器很大压力,甚至出现崩溃的情况。
二、解决思路
- 查询操作时,把结果缓存起来,下次调用同样参数时直接返回缓存结果,就会节省处理时间,查询时间越长,效果越明显。
- 非查询操作时,清空缓存,重新查询数据生成新缓存数据。
三、项目实战
Flask+Vue搭建系统,在之前的一篇文章中,实现问题的解决思路。
缓存实现
python官方文档自带缓存功能lru_cache,它可供我们传入的参数有2个maxsize和typed,如果不传则maxsize的默认值为128,typed的默认值为False。其中maxsize参数表示是的被装饰的方法最大可缓存结果数量, 如果是默认值128则表示被装饰方法最多可缓存128个返回结果,如果maxsize传入为None则表示可以缓存无限个结果。
查询时添加缓存
from functools import lru_cache # 设置缓存,减少第二次查询时间
@lru_cache()
@pysnooper.snoop('pysnooper.log') # 输出到文件
def handle_requests(qrytype: str, qryfunc: str, argc: str, Id: int = None):
dbClass = get_type_query().get(qrytype)
if not dbClass:
return jsonify(code=RET.PARAMERR, msg=u'查询类型错误')
dbcls = dbClass()
if isinstance(argc, str):
try:
argcjson = json.loads(argc)
except Exception as e:
logging.error('[handle_requests]Failed to json.load, {0}'.format(e))
logging.error(traceback.format_exc())
return jsonify(code=RET.PARAMERR, msg='Failed to json.load, {0}'.format(e))
elif isinstance(argc, dict):
argcjson = argc
else:
return jsonify(code=RET.PARAMERR, msg=u'argc查询类型错误')
# print(dir(dbcls))
if qryfunc in dir(dbcls):
if Id:
value = dbcls.__getattribute__(qryfunc)(argcjson, Id)
else:
value = dbcls.__getattribute__(qryfunc)(argcjson)
return value
else:
return jsonify(code=RET.PARAMERR, msg=qryfunc + '方法不存在,请联系开发人员')
代码说明:
- 导入lru_cache:from functools import lru_cache
- 在查询处理接口handle_requests增加装饰器lru_cache就可以实现查询结果缓存
- 相比之前没有写额外的代码,只增加的一个装饰器就实现了缓存功能
非查询时更新缓存
收到前端接口请求时,添加一个拦截函数clear_cache,判断非查询操作,就清除缓存,代码如下:
@client_page.before_request
def clear_cache():
if request.method == 'GET':
return
for key, obj in globals().items():
if not hasattr(obj, 'cache_clear'): # 判断函数是否定义了缓存
continue
obj.__getattribute__('cache_clear')()
代码说明:
- @client_page.before_request装饰器表示函数clear_cache()在进入路由之前先执行该函数
- GET表查询请求,非查询请求时清除缓存
- obj.getattribute(‘cache_clear’)()表示通过传入函数名称来执行该函数
查询效果
第一次查询耗时
第二次查询有了缓存后的耗时
第三次把"邮箱"由"1111"改成"2222"后的耗时,数据变化后,清除了缓存,重新查询了数据,耗时又增加
和redis比较
lru_cache和redis比较:
比较类型 | lru_cache | redis |
---|---|---|
缓存类型 | 内存中 | 内存中 |
分布式 | 单个进程中 | 分布式缓存 |
数据类型 | hash 参数作为key | 有多种数据类型 |
适用场景 | 小型系统 | 完整缓存解决方案 |
功能 | 功能少但使用便捷 | 功能齐全 |
python自带的缓存功能使用于稍微小型的单体应用,优点是可以很方便的根据传入不同的参数缓存对应的结果, 并且可以有效控制缓存的结果数量,在超过设置数量时根据LRU算法淘汰命中次数最少的缓存结果。缺点是没有办法对缓存过期时间进行设置。