https://gridgain.freshdesk.com/a/tickets/7043 1) Использование "lazy" флага В процессе выполнения запроса на map узле, H2 по умолчанию сначала собирает все данные в heap, и только после этого возвращает курсор, на основании которого Apache Ignite отправляет результаты запроса. Это может приводить к OOME. Проблема может быть решена путем выставления "lazy" флага [1] на уровне запроса. В этом случае, H2 будет собирать только часть результатов в heap. Данный метод применим только к неблокирующим запросам, которым не нужен полный data set для формирования финального результатов. К блокирующим операциям относятся ORDER BY, co-located GROUP BY (см. ниже). Для них флаг "lazy" не поможет избежать OOME. В будущем архитектура выполнения запросов будет переработана в целях недопущения OOME вне зависимости от состояния данного флага. 2) Co-located группировка В общем Apache Ignite осуществляет финальную группировку на reduce узле. Если заведомо известно, что данные сколоцированы по условию в GROUP BY, то можно выставить флаг SqlFieldsQuery.colocated, который заставит Apache Ignite сделать группировку локально на map узле. Это может сильно уведичить производительность путем снижения сетевого трафика между узлами, так как map узел будет отсылать на reduce узел уже сгруппированные результаты. Пример 1: SELECT SUM(salary) FROM Employee GROUP BY depatment_id В данном случае предварительная группировка будет осуществлена на map узле, так как промежуточные суммы по департаментам можно в последствии догруппировать на reduce узле. Использование флага colocated увеличит производительность незначительно. Пример 2: SELECT AVG(salary) FROM Employee GROUP BY depatment_id В данном случае по умолчанию Apache Ignite будет вынужден отправить все пары (salary, department_id) на reduce узел, сортировка на map узле невозможна. Если же пользователю заведомо известно, что Employee колоцированы по department_id (то есть на все сотрудники конкретного департамента заведомо находятся на одном и том же узле), включение флага colocated значительно уменьшит время исполнения запроса: финальная группировка будет произведена локально на map узле, а не reduce узел будет отправлено относительно небольшое количество пар (salary, AVG(department_id)). 3) Partition pruning По умолчению Apache Ignite обязан отправлять map запросы на все узлы, которые содержат данные. Но если в запросе присутствует условие равенства по атрибуту affinity key, Apache Ignite может вычислить partition, в котором находятся необходимые данные, и отправить map запрос только на узел, которому принадлежит данный partition. В перспективе данная оптимизация будет расширена на большее количество операций (напр. IN). Пример 1: CREATE TABLE employee (id BIGINT PRIMARY KEY, department_id, name VARCHAR) SELECT * FROM employee WHERE id=10; // Запрос будет отправлен только на один узел SELECT * FROM department_id WHERE id=10; // Запрос будет отправлен на все узлы Пример 2: CREATE TABLE employee (id BIGINT PRIMARY KEY, department_id, name VARCHAR) WITH "AFFINITY_KEY=department_id" SELECT * FROM employee WHERE id=10; // Запрос будет отправлен на все узлы SELECT * FROM department_id WHERE id=10; // Запрос будет отправлен только на один узел 4) Query parallelism По умолчанию Apache Ignite создает каждый индекс таблицы (PK или secondary) в виде одного B+Tree. Если запрос требует чтения большого количества записей в индексе, сканирование будет идти в один поток, что может негативно сказаться на времени исполнения запроса. Ранее в таких случаях мы рекомендовали пользователям запускать несколько экземпляров Apache Ignite на одном узле. Сейчас вместо этого мы советуем исползьовать query parallelism [2]. Это свойство кэша, которое определяет сколько деревьев будет создано для индексов. Чем больше деревьев, тем больше потоков будет исполнять map-запроса параллельно. Это может кратно ускорять выполнение запросов со сканированием. С другой стороны, это может замедлять высокоселективные запросы по индексу, так как придется делать lookup во все деревья. 5) Index inline На данный момент записи в индексных страницах в Apache Ignite имеют фиксированный размер. По умолчанию Apache Ignite включает все значения fixed-размера (bool, byte, short, int, long, float, double, date, time, timestamp, uuid) в индекс целиком. Если же в индексе присутствует поле переменной длины (string, byte[]), по умолчанию будут включены только первые 10 байт [3]. Если проиндексированные значения оказываются включены в индекс неполностью, то для их сравнения может потребоваться чтение data page соответствующей записи, что может знаичтельно замедлять как запросы, так и операции изменения данных. Поэтому, если вы собираетесь проиндексировать значения переменной длины, рекомендуется оценить их размер, и изменить inline size так, что бы большинство (или даже все) значения попадали в индекс целиком. Для этого можно использовать свойство QueryIndex.inlineSize при конфигурировании через QueryEntity, @QuerySqlField.inlineSize при конфигурировании через аннотации, либо с помощью ключевого слова INLINE_SIZE при создании индекса динамически (напр. "CREATE INDEX idx_emp_name ON emp(name) INLINE_SIZE 100"). Размер задается в байтах. Так как Ignite кодирует строковые данные с помощью UTF8, необхожимо учитывать, что некоторые символы требовать более 1 байта (напр. кириллица). В ближайшее время будут добавлены сообщения, предупреждающие пользователя, что inline index недостаточен. 6) Производительность операций над date/time типами В настоящее время Apache Ignite хранит date/time типы в виде смещения в миллисекнудах односительно "эпохи", 01-Jan-1970. Это может приводить к недостаточной производительности, если запросу требуется сравнивать даты или производить над ними какие-либо операции (нарп. больше/меньше, DATEDIFF, HOUR, и т.д.). В случае возникновения проблем со скоростью выполнения запроса рекомендуется по возможности использовать в модели целочисленные типы данных вместо date/time. Например, для запроса вида "SELECT * FROM emp WHERE birth_date < ?" атрибут "birth_date" можно определить как BIGINT, и вместо реальных дат хранить в нем смещения относительно эпохи (напр., результат java.util.Date().getTime()). В будущем мы планируем переработать формат хранения дат, что бы избавиться от проблем с производительностью. 7) Производительность IN и OR Текущая версия H2 может не использовать индексы в случае IN и OR операций. В документации даны рекомендации, как переписать запрос, что бы индекс стал использоваться [4]. 8) Контроль колокации данных В данный момент Apache Ignite не контролирует, правильно ли осуществлена колокация данных, участвующих в запросых с JOIN. В случае некорректеой колокации и выключенных distributed joins, выполнение запроса, не приведет к ошибке. Запрос будет завершен успешно, но результаты могут оказаться некорректными. В будущем мы планируем завершать такие запросы с ошибкой. Владимир. [1] https://apacheignite-sql.readme.io/docs/performance-and-debugging#section-result-set-lazy-load [2] https://apacheignite-sql.readme.io/docs/performance-and-debugging#section-query-parallelism [3] org.apache.ignite.internal.processors.query.h2.database.H2TreeIndex#IGNITE_MAX_INDEX_PAYLOAD_SIZE_DEFAULT [4] https://apacheignite-sql.readme.io/docs/performance-and-debugging#sql-performance-and-usability-considerations