2 внутренних соединения на одном столе: второе замедляет - PullRequest
0 голосов
/ 02 мая 2019

В настоящее время я работаю над переводами базы данных для моего приложения 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

...