У меня есть метод репозитория Symfony, который извлекает довольно сложный набор данных, который затем будет помещен в файл CSV классом менеджера экспорта. Я не хочу размещать весь код обработки задания экспорта, но мне удалось выделить точку, в которой запрос замедляется, поэтому мой вопрос касается любой другой альтернативы для ускорения этого запроса, а не самого кода. Таким образом, полученные данные представляют собой некоторые данные «сайта», которые имеют несколько «членств», которые затем имеют «пользователя». Итак, проблема в том, что когда мой запрос пытается присоединить информацию о пользователе к сайту, это замедляет выполнение. Это выглядит так:
$qb->leftJoin('s.memberships', 'ex_sm', 'WITH', 'ex_sm.revokedAt IS NULL');
$qb->leftJoin('ex_sm.user', 'ex_jappr', 'WITH', 'ex_sm.approverJobReactiveWeight IS NOT NULL');
Есть несколько вещей (я пробовал или думал, может помочь), чтобы упомянуть:
Существует 6 различных типов пользователей, на данный момент я только что вызвал скрипт, извлекающий тот, что указан выше, и возврат данных занял 33 минуты. Речь идет о 512 сайтах, что не является огромным набором данных. Итак, мой вопрос: существует ли другой способ DQL или Doctrine для упрощения или уменьшения количества вызовов leftJoins в таком сложном запросе и как-то улучшить производительность?
Обновлять: Я думаю, что проблема связана с индексами, поэтому я подробно расскажу об отношениях: Сущность «членство» происходит из таблицы с именем «доступ», отношение к пользователю в ее модели выглядит следующим образом:
/**
* The user this membership encapsulates.
*
* @ORM\ManyToOne(targetEntity = "User", inversedBy = "siteMemberships", cascade = {"persist"})
* @ORM\JoinColumn(name = "security_identity_id", referencedColumnName = "id")
*
* @var User
*/
protected $user;
Вот скриншот индексов, присвоенных столбцам «security_identity_id».
И связанный пользователь происходит из таблицы «пользователь», имеющей это отношение, указывающее на членство
/**
* @ORM\OneToMany(targetEntity = "SiteMembership", mappedBy = "user", cascade = {"persist"}, fetch = "EXTRA_LAZY")
*/
protected $siteMemberships;
Первичный ключ — это «id» в сущности. Надеюсь, это даст лучшее представление о проблеме. Я не эксперт по sql, но перепробовал все, что нашел и смог понять до сих пор.
Обновлять: Вот выполненный запрос:
SELECT s0_.name AS name_0, s0_.id AS id_1, GROUP_CONCAT(DISTINCT u1_.name SEPARATOR ', ') AS sclr_2 FROM site s0_
LEFT JOIN access a2_ ON s0_.id = a2_.entity_id
AND a2_.type IN ('site_member')
AND (a2_.revoked_at IS NULL)
LEFT JOIN user u1_ ON a2_.security_identity_id = u1_.id
AND (a2_.approver_job_reactive_weight IS NOT NULL)
Это возвращает первую запись сайта с присоединенным к ней членством и пользовательскими правами. Но даже фетиш этого одного единственного ряда занимает более 2 минут.
Вот информация о создании таблицы таблицы доступа (членства).
'CREATE TABLE `access` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`buddy_id` int(11) DEFAULT NULL,
`security_identity_id` int(11) DEFAULT NULL,
`revoked_at` datetime DEFAULT NULL,
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL,
`type` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`approver_job_reactive_weight` int(11) DEFAULT NULL,
`entity_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `access_idx` (`type`,`security_identity_id`,`entity_id`,`buddy_id`),
KEY `IDX_6692B54395CE8D6` (`buddy_id`),
KEY `IDX_6692B54DF9183C9` (`security_identity_id`),
KEY `IDX_6692B5481257D5D` (`entity_id`),
KEY `idx_revoked_id_approver_type` (`revoked_at`,`entity_id`,`approver_job_reactive_weight`,`approver_job_planned_weight`,`type`),
KEY `idx_user_site_access` (`revoked_at`,`security_identity_id`,`buddy_id`,`type`),
KEY `idx_user` (`security_identity_id`),
KEY `idx_user_id` (`security_identity_id`),
CONSTRAINT `FK_6692B54DF9183C9` FOREIGN KEY (`security_identity_id`) REFERENCES `user` (`id`)
)
ENGINE=InnoDB AUTO_INCREMENT=262441 DEFAULT CHARSET=utf8
COLLATE=utf8_unicode_ci'
Я удалил некоторые столбцы, которые не имеют отношения к делу.






Вы присоединились ко многим сторонам. Это причина замедления
$qb->leftJoin('s.memberships', 'ex_sm', 'WITH', 'ex_sm.revokedAt IS NULL');
больше членств, тем медленнее ваш запрос. Я не знаю, как сделать полный запрос, но вы можете запустить запрос из таблицы членства или сделать второй запрос.
Привет, Мехмет, спасибо за ответ. Я удалил все дополнительные материалы, так что сейчас весь запрос — это то, что вы видите. Если выбрать столбец присоединенного членства на сайте и удалить часть jobApprover, запрос будет успешно выполнен в течение 4 секунд. Это не супер быстро для объема возвращаемых данных, но все же не полчаса. Проблема, кажется, начинается с присоединения пользователя.
«Это возвращает первую запись сайта» — я не вижу LIMIT 1; как вы находите "первое"?
привет. Я думал, что ваша проблема возникла из-за многих побочных запросов. Однако я видел, что вы добавили индексы. Это поможет ускорить запрос, но размер вашей базы данных увеличится. хорошего дня. Спасибо за Рика Джеймса :)
При выполнении LEFT JOINON нужно сказать, как связаны таблицы. В предложении WHERE обычно есть IS NULL или IS NOT NULL, чтобы указать, следует ли исключить или включить правые строки.
LEFT JOIN и INNER JOIN в основном имеют одинаковую скорость. Но мне нужно увидеть индексы (SHOW CREATE TABLE) и SQL SELECT, чтобы увидеть, есть ли другие проблемы.
Более
Заменять
KEY `IDX_6692B5481257D5D` (`entity_id`),
С
INDEX(entity_id, type, revoked_at)
Привет, Рик, только что обновил вопрос. Пока я смотрел на запрос, мне пришло в голову, что проблема может быть вызвана GRUP CONCAT имен. Попробую без этого. Также интересно, должен ли быть какой-либо индекс имен в пользовательской таблице?
Нет, удаление конкатенации не добавило никаких улучшений.
Ну, что-нибудь еще попробовать.
Не уверен, что правильно понимаю, что вы подразумеваете под заменой. Вы хотите удалить этот ключ и создать другой, который будет использоваться тремя столбцами, или просто использовать один и тот же существующий ключ со столбцами «type» и «revoked_at»?
Это то, что я добавил новый ключ индекса для этих 3 столбцов. Время выполнения, ранее составлявшее более получаса, теперь сокращено до 8 секунд. Кажется, мне действительно не хватает хорошего понимания того, как работают эти индексы. Поэтому я предполагаю, что хорошей практикой является наличие общего ключа индекса среди столбцов, которые участвуют в одной и той же логике левого соединения. Так ли это?
Мне потребовались годы, чтобы стать достаточно опытным, чтобы я мог обычно смотреть на SELECT и выстукивать оптимальное INDEX. Некоторые из моих приемов есть в моей Index Cookbook
Это действительно приятно, спасибо за это.
Предоставьте сгенерированный SQL.