Spring数据mongodb DbRef缓存

问题描述 投票:0回答:1

我有一个获取 10k 文档的查询,每个文档都有另一个文档的 dbref。此查询大约需要 5 秒才能运行,并且是不可接受的。在调试和日志记录期间,我发现 mongo 驱动程序会为每个 DbRef 往返数据库。

我的DbRef文档大部分主要文档都是相同的。我想为此使用缓存,并且不要为每个文档进行额外的 mongo 往返。

问题是:执行主查询时可以缓存 DbRefs 吗?

mongodb spring-data-mongodb dbref
1个回答
0
投票

几年后,我遇到了同样的问题,并最终找到了一种设置“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 时自动删除缓存条目。 (根据内部实现,似乎清除也会在映射读/写时触发,因此与巨大的性能提升相比,过时的条目在您的应用程序中不应该引起太多关注)。

© www.soinside.com 2019 - 2024. All rights reserved.