把查询延迟从2秒降到80毫秒:AI绘画关键词网站的优化历程
项目上线初期,关键词推荐服务撑不住并发,用户输入'星空下的城市夜景',系统要花两秒多才能返回结果。关键词库很快涨到十万级别,内存告警频繁,响应速度越来越慢。
最初用的是 TF-IDF 加关键词匹配,效果差强人意。TF-IDF 精度低,语义关联基本没有——'赛博朋克'和'未来主义'被当成两个完全不同的词。而且每次请求都要扫全库做相似度计算,CPU 和 I/O 双双吃紧。
后来我们对比了几种方案,下面的表格是当时记录的实测数据(单机环境,JMeter 100 并发):
| 方案 | 准确率 | 查询延迟 | 内存占用 | 适合场景 |
|---|---|---|---|---|
| TF-IDF | 62% | 120ms | 低 | 小规模精确匹配 |
| Word2Vec | 78% | 200ms | 中 | 中等规模语义搜索 |
| BERT+FAISS | 89% | 350ms | 高 | 大规模精准语义搜索 |
单看延迟,BERT 最慢,但准确率高出不少。我们决定在 BERT 基础上做加速。
向量化与索引
先用 SentenceTransformers 的多语言 MiniLM 把关键词转成 384 维向量,再用 FAISS 建内积索引。代码大致如下:
from sentence_transformers import SentenceTransformer
import faiss
import numpy as np
model = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')
keywords = ["赛博朋克", "水墨风格", "浮世绘"] # 实际有10万+
keyword_vectors = model.encode(keywords)
dimension = keyword_vectors.shape[1]
index = faiss.IndexFlatIP(dimension)
index.add(keyword_vectors)
faiss.write_index(index, "keyword_vectors.faiss")
写入文件是为了重启时不用重新生成向量,直接加载。
缓存层
不加缓存时,BERT+FAISS 的 QPS 不到 30。我们加了一层 Redis,并塞了个布隆过滤器防止缓存穿透。布隆过滤器不是万能药,但它确实把大量无效查询挡在了外面。
import redis
import json
from datetime import timedelta
r = redis.Redis(host='localhost', port=6379, db=0)
def ():
r.bf().exists(, query):
cache_key =
result = r.get(cache_key)
result:
json.loads(result)
():
r.bf().add(, query)
r.setex(
,
timedelta(seconds=ttl),
json.dumps(results)
)

