前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >GraphQL实践6——Netflix Dgs Graphql N+1问题

GraphQL实践6——Netflix Dgs Graphql N+1问题

原创
作者头像
F嘉阳
发布2022-10-28 20:53:46
5700
发布2022-10-28 20:53:46
举报
文章被收录于专栏:graphqlgraphql

N+1问题介绍

对于上一篇文章样例,如果要获取每个电影的演员名单,要执行如下动作

  • 查询所有电影清单
  • 遍历N个电影,查询对应电影的演员名单

总查询开销为N+1次查询,代价非常大,效率低

优化方案 DataLoader

对于一对一表关联的情况且每个关联对象只有一个值,可以直接使用BatchLoader

但实际上,对于大部分表关联情况,通常为一对多或者多对多,不保证每个关联都有值,此时需要使用MappedBatchLoader维护关联关系

维护多对多实体关系Map

代码语言:txt
复制
public interface ActorService {
    Map<Integer, List<Actor>> listByFilmId(Collection<Integer> filmIds);
}
代码语言:txt
复制
@Slf4j
@Service
@RequiredArgsConstructor
public class ActorServiceImpl implements ActorService {

    private final ActorRepository actorRepository;

    private final FilmActorRepository filmActorRepository;

    @Override
    public Map<Integer, List<Actor>> listByFilmId(Collection<Integer> filmIds) {
        List<FilmActor> filmActors = filmActorRepository.listByFilmId(filmIds);
        if (CollectionUtils.isEmpty(filmActors)) {
            return Collections.emptyMap();
        }
        Map<Integer, List<FilmActor>> filmActorMap = filmActors.stream().collect(Collectors.groupingBy(FilmActor::getFilmId));
        List<Integer> actorIds = filmActors.stream().map(FilmActor::getActorId).distinct().toList();
        List<Actor> actors = actorRepository.listByIds(actorIds);
        Map<Integer, Actor> actorsMap = Stream.ofNullable(actors).flatMap(Collection::stream).collect(Collectors.toMap(Actor::getActorId, Function.identity(), (e1, e2) -> e1));
        Map<Integer, List<Actor>> result = Maps.newHashMapWithExpectedSize(filmIds.size());
        filmActorMap.forEach((k, v) -> {
            List<Actor> actorList = Stream.ofNullable(filmActorMap.get(k)).flatMap(Collection::stream).map(e -> actorsMap.get(e.getActorId())).toList();
            result.put(k, actorList);
        });
        return result;
    }
}

Dgs MappedBatchLoader 配置

代码语言:txt
复制
@DgsDataLoader(name = "actors")
@RequiredArgsConstructor
public class FilmActorsDataLoader implements MappedBatchLoader<Integer, List<Actor>> {

    private final ActorService actorService;

    @Override
    public CompletionStage<Map<Integer, List<Actor>>> load(Set<Integer> keys) {
        return CompletableFuture.supplyAsync(() -> actorService.listByFilmId(keys));
    }
}

测试

访问http://localhost:8080/graphiql即可看到在线查询页面

image-20221027223715143.png
image-20221027223715143.png

此时执行嵌套查询只会查询2次,一次查电影列表,一次查所有电影的Actor列表,非常快速

image-20221027223756097.png
image-20221027223756097.png

总结

使用BatchLoader实现对N+1问题优化,但还有一个潜在问题,即大数据分页,该样例中,有1000个电影,对应5000+演员,查询一次获取全量数据,对数据库压力较大,也不符合实际场景,还需要进行分页优化

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • N+1问题介绍
  • 优化方案 DataLoader
    • 维护多对多实体关系Map
      • Dgs MappedBatchLoader 配置
      • 测试
      • 总结
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档