Проблема N + 1 возникает, когда мы используем данные, возвращенные из базы данных, для создания списка дальнейших запросов к базе данных.
Это означает, что если вы получаете список результатов запроса, то для каждого результата вы будете делать другой запрос к базе данных.
В этом случае N
обозначает количество результатов, возвращенных из базы данных, а 1
обозначает начальный запрос, который был сделан.
Чтобы уточнить: проблема в том, что для каждого из возвращенных N
результатов нам нужно будет сделать другой запрос, поэтому будет N
запросов к базе данных плюс 1
, который был первоначальным запросом.
Это легко может стать узким местом в производительности.
Пример ситуации N + 1
Вы хотите запросить оценки для всех учащихся, посещающих школу. Проблема N + 1 возникнет, если вы сначала запросите всех учеников, которые ходят в эту школу, а затем создадите цикл, который запросит оценки для каждого ученика. , которые возвращались по одному.
Вот пример псевдокода:
const students = `SELECT * FROM students` students.forEach(student => `SELECT * FROM grades WHERE student_id = ${student.student_id}`)
Традиционное решение проблемы N + 1
Общепринятое решение - группировать ваши запросы. Это означает, что вы составляете список идентификаторов результатов первоначального запроса, а затем ваш второй запрос динамически заполняется этим списком. Это решение сокращает количество запросов к базе данных с N + 1 до 2.
Пример псевдокода:
const students = `SELECT * FROM students` const studentIds = students.map(student => student.student_id) const grades = `SELECT * FROM grades WHERE student_id IN ${studentIds}`
Чтобы уточнить: в приведенном выше коде мы сначала запрашиваем всех студентов, а затем составляем список (также называемый массивом) всех их идентификаторов. Затем мы делаем запрос к базе данных для всех оценок, но также предоставляем ему наш список идентификаторов учащихся и просим базу данных отфильтровать результаты для нас по этим Идентификаторы.
Теперь это намного эффективнее!
Почему это решение не работает с GraphQL
Общепринятой реализацией GraphQL является Apollo, использующий Resolvers. Каждый преобразователь имеет доступ только к возвращаемому значению своего родителя, а это означает, что вы не можете пойти и составить список идентификаторов, как мы сделали выше, а затем передать их в пакетный запрос.
Так как же решить эту проблему? Мы используем пакет под названием DataLoader.
Как решить N + 1 задач в GraphQL
DataLoader - это пакет, который выполняет пакетную загрузку объектов из хранилища данных. Он также имеет кеш мемоизации, который не позволяет нашему приложению загружать один и тот же объект несколько раз из одного запроса GraphQL. Это также позволяет нам получать сразу несколько объектов с помощью пакетной операции, что позволяет избежать проблемы N + 1.
В следующей статье я подробно расскажу, как реализовать DataLoader в вашем проекте GraphQL.
Если вам понравилась эта статья, пожалуйста, аплодируйте! 👏🏼 Хлопки меня радуют;)