前情提要
遗留问题
- 优化同步速度,目前大约30秒,不是一个生产速度
这次来解决遗留问题
通过console,发现两个地方特别耗时,一个是查询数据,另一个是初始化ivf
/**
* 同步redis
* @param storeCode
*/
async syncRedis(storeCode: string) {
let s = Date.now();
const featureDatabase = await this.findAll(storeCode);
let e = Date.now();
console.log(`查询耗时1:${e - s}ms`);
const ids = featureDatabase.map(({ id }) => id);
await this.redisService.set(`${storeCode}-featureDatabase`, JSON.stringify(ids));
s = Date.now();
const url = 'http://localhost:5000/sync'; // Python 服务的 URL
await firstValueFrom(this.httpService.post(url, { data: featureDatabase, storeCode }));
e = Date.now();
console.log(`查询耗时3:${e - s}ms`);
}
解决方案
优化查询速度
之前使用的是FIND_IN_SET方法对类似1,2,3这样的数据进行包含条件的查询,速度太慢了,优化后:
/**
* 查询所有
* @param storeCode
*/
async findAll(storeCode: string) {
return await this.featureRepository.createQueryBuilder('feature')
.select(['feature.id', 'feature.features'])
.where('feature.storeCode REGEXP :storeCode', { storeCode: `(^|,)${storeCode}(,|$)` })
.getMany();
}
效果提升了一倍:
优化ivf初始化的速度
当前的初始化方法
def __init__(self, features, nlist=100, m=16, n_bits=8):
d = features.shape[1]
# 创建量化器
quantizer = faiss.IndexFlatL2(d) # 使用L2距离进行量化
self.index = faiss.IndexIVFPQ(quantizer, d, nlist, m, n_bits)
# 训练索引
self.index.train(features)
self.index.add(features) # 将特征向量添加到索引中
优化方法:
- 增加并行化处理
# 设置线程数,例如使用所有可用的CPU核心
faiss.omp_set_num_threads(num_threads) # num_threads 是你希望使用的线程数量
- 减少索引的复杂度
减少nlist和m的值,但这样会损失精度,先不采用 - 使用增量添加数据
分批处理可以分散压力,同时利用数据流式处理的优势。
batch_size = 1000 # 每次处理1000个特征
for i in range(0, len(features), batch_size):
self.index.add(features[i:i+batch_size])
- 更换其他索引类型
self.index = faiss.IndexIVFFlat(quantizer, d, nlist)
下一步
- 新建store_feature表,关联storeCode和featureId表,对数据库进行规范化,创建一个新的表来映射storeCode与feature的关系,从而可以使用简单的WHERE条件来充分利用索引
- 实现对特征向量ivf的增删改查