Решение проблемы N+1
Артем Житник
Используя API мы видим красивый фасад. Что за ним скрывается, какая сложность? А может все работает волшебным образом?
Допустим нам нужно получить через API список элементов инфоблока, у этих элементов несколько свойств, среди которых могут быть привязки к другим элементам инфоблока, у которых тоже выберем набор свойств. Как с этим справляется backend, давайте разбираться.
GraphQL API состоит из набора типов. Экземпляры типов объединяются в структуры и отдаются клиенту в форме json. Есть базовые типы, скалярные: строка, целое и дробное числа, булевый и т.д. Есть типы-объекты, в нашем случае: элемент инфоблока, раздел инфоблока, элемент highload блока, свойство элемента, пользовательское свойство раздела и т.д. Последние, кстати, делятся на целую кучу вариаций: список, файл, справочник и много чего еще. Даже тип для видео есть. Также имеется специальный тип список элементов (какого-то другого типа).
У каждого типа на backend есть своя функция-relolver. Если упростить, она выдергивает данные из базы и приводит их формат соответствующий типу. Получается, если мы выбираем одним запросом к базе N элементов, у каждого из них имеются свойства типов-объектов, которые также нужно вытаскивать из базы, нужно еще N запросов. Эти запросы может быть и легкие, но их много, что может сказываться на производительности. Это так называемая проблема N+1. Она часто возникает в ORM системах.
А что если вместо N, сделать 1 запрос и выбрать все зависимые объекты? Тогда у нас получается всего 1+1 запросов. В graphql-php, на которой строится наш модуль, используются подход с буферизацией получения данных. Подробнее о реализации можно почитать в их доках.
Вы можете сказать, можно ведь обойтись одним запросом. Выбрать сущности с join соединением, например значения свойств через CIblockElement. Возможно, для каких-то узко специализированных методов, такой подход оправдан. Но в нашем случае, API достаточно гибкий, типы насколько это возможно изолированы друг от друга, элементы инфоблока - отдельно запрашиваются, свойства - отдельно. Кстати, свойства еще могут в отдельных таблицах хранится, что усложняет вариант с одним запросом.