Python中如何优化单次请求中计算并返回相似资源时的性能问题?
目前系统有个场馆模块,每次访问单个场馆详情页的时候,有个相似场馆的数据段 similar_staduims ,这个是每次请求时候,都会去数据库取出所有场馆,然后根据相应的相似权重计算并排序,然后取前几个的值。这样在数据量少的时候没有很大影响(也本着创业公司项目早期 make job done 的想法,先完成需求)。目前场馆数目到达一万的时候,就开始出现请求超时的问题。
我有考虑到每次请求应该减少计算次数或者是直接不应该把计算过程放在请求响应阶段,权重排序应该作为一个计算队列,定时,或者每当有场馆更改的时候出发全局计算,然后把计算结果存储起来,每次请求只需要取出计算结果即可。但是目前没有一个很清晰的想法。所以想发帖求助。
技术栈:
- backend: flask + python3
- database: postgresql 9.6
- cache:redis
先谢谢各位大大。
Python中如何优化单次请求中计算并返回相似资源时的性能问题?
up
核心思路: 减少重复计算,用空间换时间。
具体做法:
- 缓存计算结果:用
functools.lru_cache或字典缓存相似度计算结果,避免重复计算相同资源对。 - 向量化计算:如果资源能用向量表示(如词向量、特征向量),用 NumPy 做批量矩阵运算,比循环快得多。
- 提前索引:对资源建索引(比如用 FAISS 或 annoy),用近似最近邻搜索快速找相似项,别实时全量计算。
- 并行计算:用
concurrent.futures或joblib把计算任务拆到多个核上跑。
示例代码(用缓存+向量化):
import numpy as np
from functools import lru_cache
from typing import List
# 假设每个资源是特征向量
resources: List[np.ndarray] = [...] # 你的资源列表
@lru_cache(maxsize=None)
def cached_similarity(idx1: int, idx2: int) -> float:
"""缓存两个资源间的余弦相似度"""
vec1, vec2 = resources[idx1], resources[idx2]
dot = np.dot(vec1, vec2)
norm = np.linalg.norm(vec1) * np.linalg.norm(vec2)
return dot / (norm + 1e-8)
def find_similar(target_idx: int, top_k: int = 5) -> List[int]:
"""找最相似的top_k个资源"""
sims = [
(i, cached_similarity(target_idx, i))
for i in range(len(resources)) if i != target_idx
]
sims.sort(key=lambda x: x[1], reverse=True)
return [idx for idx, _ in sims[:top_k]]
一句话建议: 上缓存和向量化,别在请求里傻算。
既然用到了 redis
一个场馆的相似场馆应该在一定时间内固定的才对,不需要每次计算,除非是有新的相似场馆增加,这个为基础
1\增加缓存,场馆->相似场馆 这个映射关系去处理
2\异步计算,定期更新缓存
这样不需要每次重新计算,虽然会丢失一些时效性,但这个可以根据 2 的定期时间来减少误差
mark,感觉楼主思路是对的啊,场馆录入的时候进行计算,计算完成修改数据库状态表明其可用?
你的考虑和我想法差不多,如果做成你这样的触发机制其实也很简单
用 redis 的订阅或者阻塞队列作为信号源
启一个服务专门用来计算,订阅 topic 或者 block pop 来监听计算信号就行了,这个服务只做计算,主服务只读
> 除非是有新的相似场馆增加,这个为基础 这个相似度,其实都是全局计算后根据匹配的分数排序的。所以如果是计算相似结果这个过程,就看是每次某个新增场馆后者修改现有场馆信息后,要实时触发全局计算,还是对时效性没有这么苛刻,每天定时计算、更新全局的权重数据。我更倾向于后者。感谢你的梳理啊~~
你的思路类似数据变动触发机制吧。
你相似场馆难道还是实时变动的吗
起程序之前,直接算好整个相似矩阵,之后请求的时候直接读取矩阵就行了。
然后每天更新一次矩阵,程序重读一次。
每天跑一次计算任务或者说建立触发重新计算的机制,缓存起来,平时只读。不是都是这么做的么😏


