我有一个获取 10k 文档的查询,每个文档都有另一个文档的 dbref。此查询大约需要 5 秒才能运行,并且是不可接受的。在调试和日志记录期间,我发现 mongo 驱动程序会为每个 DbRef 往返数据库。
我的DbRef文档大部分主要文档都是相同的。我想为此使用缓存,并且不要为每个文档进行额外的 mongo 往返。
问题是:执行主查询时可以缓存 DbRefs 吗?
几年后,我遇到了同样的问题,并最终找到了一种设置“dbref 缓存”的方法。
首先我们制作一个
CachedDbRefResolver
来扩展 Spring 的 DefaultDbRefResolver
:
import org.apache.commons.collections4.map.ReferenceMap;
public class CachedDbRefResolver extends DefaultDbRefResolver {
private final ReferenceMap<DBRef, Object> entityCache = new ReferenceMap<>();
public CachedDbRefResolver(MongoDatabaseFactory mongoDbFactory) {
super(mongoDbFactory);
}
/**
* Copied from DefaultDbRefResolver, but for non-lazy dbrefs, the value is cached fetched from there if present.
*/
@Override
public Object resolveDbRef(MongoPersistentProperty property, @Nullable DBRef dbref, DbRefResolverCallback callback, DbRefProxyHandler handler) {
Assert.notNull(property, "Property must not be null!");
Assert.notNull(callback, "Callback must not be null!");
Assert.notNull(handler, "Handler must not be null!");
if (isLazyDbRef(property)) {
return super.resolveDbRef(property, dbref, callback, handler);
} else if (entityCache.containsKey(dbref)) {
return entityCache.get(dbref);
}
var entity = callback.resolve(property);
if (dbref == null || entity == null) {
return entity;
}
entityCache.put(dbref, entity);
return entity;
}
// copied from DefaultDbRefResolver
private boolean isLazyDbRef(MongoPersistentProperty property) {
return property.getDBRef() != null && property.getDBRef().lazy();
}
}
然后在 Spring Mongo 配置中,我们在构建
CachedDbRefResolver
时使用新的 MappingMongoConverter
:
@Configuration
public class SpringMongoConfiguration extends AbstractMongoClientConfiguration {
...
@Override
@Bean
@NotNull
public MappingMongoConverter mappingMongoConverter(MongoDatabaseFactory databaseFactory,
MongoCustomConversions customConversions,
MongoMappingContext mappingContext) {
DbRefResolver dbRefResolver = new CachedDbRefResolver(databaseFactory);
MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, mappingContext);
converter.setCustomConversions(customConversions);
converter.setCodecRegistryProvider(databaseFactory);
return converter;
}
}
这应该在 Spring 中为您设置一个 dbref 缓存!
请注意,缓存使用 apache commons-collection4 包中的“ReferenceMap”。 ReferenceMap 使用 Java“弱引用”来存储缓存的 dbref 条目,这允许垃圾收集器在不再有对象引用这些 dbref 时自动删除缓存条目。 (根据内部实现,似乎清除也会在映射读/写时触发,因此与巨大的性能提升相比,过时的条目在您的应用程序中不应该引起太多关注)。