В настоящее время я работаю над переводами базы данных для моего приложения Symfony 4 и использую расширение Transurable (http://atlantic18.github.io/DoctrineExtensions/doc/translatable.html) для Doctrine. Оно создает таблицу перевода для моих программ со следующей структурой:
CREATE TABLE `equipment_translations` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`locale` varchar(8) COLLATE utf8mb4_unicode_ci NOT NULL,
`object_class` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`field` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL,
`foreign_key` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
`content` longtext COLLATE utf8mb4_unicode_ci,
PRIMARY KEY (`id`),
KEY `equipment_translation_idx` (`foreign_key`,`locale`,`field`),
) ENGINE=InnoDB AUTO_INCREMENT=5973 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
У меня есть 2 разных поля, которые нужно перевести: name
и description
Переводы для обоих сохраняются в одной таблице с соответствующим значением field
.
К сожалению, выполнение внутренних объединенийоба результата приводят к очень низкой производительности следующего запроса:
SELECT *
FROM equipment e5_
INNER JOIN equipment_translations t6_
ON t6_.foreign_key = e5_.id
AND t6_.locale = 'en'
AND t6_.field = 'name'
INNER JOIN equipment_translations t7_
ON t7_.foreign_key = e5_.id
AND t7_.locale = 'en'
AND t7_.field = 'description'
WHERE e5_.engine = %ID%;
Это автоматически сгенерированный запрос (конечный результат затем обрабатывается доктриной в моих объектах ORM), для которого я удалил некоторые операторы длясделать это проще.
Теперь, если я использую только одно из этих объединений, запрос занимает 0,01 с:
SELECT *
FROM equipment e5_
INNER JOIN equipment_translations t6_
ON t6_.foreign_key = e5_.id
AND t6_.locale = 'en'
AND t6_.field = 'name'
WHERE e5_.engine = %ID%;
Однако с обоими объединениями это занимает примерно 0,5 с. Это ужеплохо, но у меня есть более сложные запросы, которые занимают десятки секунд, как это (без объединения переводов, они находятся в диапазоне миллисекунд) ... Этоявно неприемлемо для веб-сайта.
SQL EXPLAIN выводит следующее:
+---+--------+-----+----+--------+--------------------------------+---------------------------+----+-------------------------+------+-------+-----------------------+----------------------------------------+
| 1 | SIMPLE | t6_ | \N | ref | equipment_translation_idx,idx2 | equipment_translation_idx | 34 | const | 2937 | 10.00 | Using index condition | |
+---+--------+-----+----+--------+--------------------------------+---------------------------+----+-------------------------+------+-------+-----------------------+----------------------------------------+
| 1 | SIMPLE | e5_ | \N | eq_ref | PRIMARY,engine | PRIMARY | 4 | autopm2.t6_.foreign_key | 1 | 5.00 | Using where | |
| 1 | SIMPLE | t7_ | \N | ALL | equipment_translation_idx,idx2 | \N | \N | \N | 5874 | 0.50 | Using where | Using join buffer (Block Nested Loop) |
+---+--------+-----+----+--------+--------------------------------+---------------------------+----+-------------------------+------+-------+-----------------------+----------------------------------------+
Затем я попытался использовать FORCE INDEX FOR JOIN (equipment_translation_idx)
, что позволило MySQL фактически использовать индекс при втором соединении, носкорость не увеличилась вообще.
Конечно, если я использую только одно соединение, чтобы объединить оба field
с, запрос будет быстрым, но тогда я получу двойные строки, и Doctrine не сможет это гидрировать.в мои сущности.
Итак, мой вопрос: почему это происходит, и могу ли я что-то с этим сделать или, может быть, есть другой способ заставить это работать быстро?
Протестированные версии MySQL:5.7.22 и 5.5.62