SQL тройное соединение на одной и той же таблице медленно - PullRequest
1 голос
/ 12 июня 2019

У меня есть таблица, показанная ниже

 CREATE TABLE `xcpRush2_SandraTriplets` (
   `id` int(11) NOT NULL AUTO_INCREMENT,
  `idConceptStart` int(11) NOT NULL,
  `idConceptLink` int(11) NOT NULL,
  `idConceptTarget` int(11) NOT NULL,
  `flag` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_name` (`idConceptStart`,`idConceptLink`,`idConceptTarget`),
  KEY `idConceptStart` (`idConceptStart`,`idConceptLink`,`idConceptTarget`),
  KEY `idConceptStart_4` (`idConceptStart`),
  KEY `idConceptTarget` (`idConceptTarget`),
  KEY `idConceptLink` (`idConceptLink`,`idConceptTarget`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

Данные будут выглядеть в скрипте db: https://www.db -fiddle.com / f / ejXP7qgvwNqAZeuaN3DFNz / 3

Как вы можете видеть, он полностью включен в несколько столбцов.

В моей таблице около 800k idConceptStart, удовлетворяющих условию

idConceptLink = 5 idConceptTarget = 14500 AND
idConceptLink = 3 idConceptLink = 14504 AND
idConceptLink = 12 idConceptLink = 11

Когда я выполняю этот запрос

SELECT * FROM  xcpRush2_SandraTriplets l    
   JOIN  xcpRush2_SandraTriplets link1 ON link1.idConceptStart = l.idConceptStart  
   JOIN  xcpRush2_SandraTriplets link2 ON link2.idConceptStart = link1.idConceptStart

    WHERE 
       l.idConceptLink = 5  AND 
       l.idConceptTarget = 14500 AND 
       l.flag != 1 AND 

       link2.flag != 1 AND 
       link2.idConceptLink = 3 AND 
       link2.idConceptTarget = 14504 AND 

       link1.flag != 1 AND 
       link1.idConceptTarget = 12 AND 
       l.idConceptLink = 11  

    ORDER BY l.idConceptStart DESC  LIMIT 10 

Вот SQL-объяснение SQL Explain

Запрос занимает около 30 секунд (!) Для визуализации

Но если я уберу это (итолько это)

 link2.idConceptLink = 3 AND link2.idConceptTarget =14504

тогда запрос занимает 20 миллисекунд для визуализации

    SELECT * FROM  xcpRush2_SandraTriplets l    
   JOIN  xcpRush2_SandraTriplets link1 ON link1.idConceptStart = l.idConceptStart  
   JOIN  xcpRush2_SandraTriplets link2 ON link2.idConceptStart = l.idConceptStart 
   WHERE 
      l.idConceptLink = 5 AND 
      l.idConceptTarget = 14500 AND 
      l.flag != 1  AND 

      link2.flag != 1 AND       

      link1.flag != 1 AND 
      link1.idConceptTarget = 12 AND 
      link1.idConceptLink = 11  

    ORDER BY l.idConceptStart DESC  LIMIT 10 

SQL Explain

Я озадачен, потому чтотаблица индексируется по idConceptLink, idConceptTarget, и каждый из этих запросов, взятых отдельно, очень быстро отображает <20 мс </p>

Кроме того, все пары idConceptLink, idConceptTarget в запросе возвращают тяжелыйколичество строк (не только link2.idConceptLink = 3 И link2.idConceptTarget = 14504)

Не могли бы вы помочь мне определить горлышко бутылки?

Редактировать

После дополнительных выводов в комментариях проблема, кажется, находится в ORDER BY.В зависимости от того, присоединяюсь ли я к l.idConceptStart или link1.idConceptStart или link2.idConceptStart, запрос выполняется медленно.В моем случае ORDER BY link2.idConceptStart работает медленно.

Структура индекса следующая:

CREATE TABLE `xcpRush2_SandraTriplets` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `idConceptStart` int(11) NOT NULL,
  `idConceptLink` int(11) NOT NULL,
  `idConceptTarget` int(11) NOT NULL,
  `flag` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_name` (`idConceptStart`,`idConceptLink`,`idConceptTarget`),
  KEY `idConceptStart` (`idConceptStart`),
  KEY `idConceptTarget` (`idConceptTarget`),
  KEY `idConceptLink` (`idConceptLink`,`idConceptTarget`)
) ENGINE=InnoDB AUTO_INCREMENT=5747878 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

Индекс

xcprush2_sandratriplets 0   PRIMARY 1   id  A   5207892 NULL    NULL        BTREE       
xcprush2_sandratriplets 0   idx_name    1   idConceptStart  A   1243366 NULL    NULL        BTREE       
xcprush2_sandratriplets 0   idx_name    2   idConceptLink   A   5207936 NULL    NULL        BTREE       
xcprush2_sandratriplets 0   idx_name    3   idConceptTarget A   5207936 NULL    NULL        BTREE       
xcprush2_sandratriplets 1   idConceptStart  1   idConceptStart  A   1122352 NULL    NULL        BTREE       
xcprush2_sandratriplets 1   idConceptTarget 1   idConceptTarget A   123870  NULL    NULL        BTREE       
xcprush2_sandratriplets 1   idConceptLink   1   idConceptLink   A   5   NULL    NULL        BTREE       
xcprush2_sandratriplets 1   idConceptLink   2   idConceptTarget A   154480  NULL    NULL        BTREE

Indexes

Запрос медленный , когда я делаю

 SELECT  l.idConceptStart, l.idConceptLink, l.`idConceptTarget` FROM  xcpRush2_SandraTriplets l  JOIN  xcpRush2_SandraTriplets link1 ON link1.idConceptStart = l.idConceptStart  JOIN  xcpRush2_SandraTriplets link2 ON link2.idConceptStart = l.idConceptStart 
    WHERE l.idConceptLink = 5  
    AND l.idConceptTarget = 14500
    AND l.flag != 1 
     AND link1.flag != 1 AND 
            link1.idConceptTarget =14504 AND link1.idConceptLink = 3 AND link2.flag != 1 AND 
            link2.idConceptTarget =12 AND link2.idConceptLink = 11  ORDER BY  link2.idConceptStart DESC  LIMIT 1000 OFFSET 0

Вот структура EXPLAIN

1   SIMPLE  link1   NULL    ref idx_name,idConceptStart,idConceptTarget,idConceptLink   idConceptTarget 4   const   1611256 18.00   Using where; Using temporary; Using filesort
1   SIMPLE  l   NULL    eq_ref  idx_name,idConceptStart,idConceptTarget,idConceptLink   idx_name    12  sandra.link1.idConceptStart,const,const 1   90.00   Using where
1   SIMPLE  link2   NULL    eq_ref  idx_name,idConceptStart,idConceptTarget,idConceptLink   idx_name    12  sandra.link1.idConceptStart,const,const 1   90.00   Using where

Slow explain

Запрос быстрый , когда я делаю

SELECT  l.idConceptStart, l.idConceptLink, l.`idConceptTarget` FROM  xcpRush2_SandraTriplets l  JOIN  xcpRush2_SandraTriplets link1 ON link1.idConceptStart = l.idConceptStart  JOIN  xcpRush2_SandraTriplets link2 ON link2.idConceptStart = l.idConceptStart 
    WHERE l.idConceptLink = 5  
    AND l.idConceptTarget = 14500
    AND l.flag != 1 
     AND link1.flag != 1 AND 
            link1.idConceptTarget =14504 AND link1.idConceptLink = 3 AND link2.flag != 1 AND 
            link2.idConceptTarget =12 AND link2.idConceptLink = 11  ORDER BY  l.idConceptStart DESC  LIMIT 1000 OFFSET 0

Вот структура EXPLAIN

   1    SIMPLE  l   NULL    index   idx_name,idConceptStart,idConceptTarget,idConceptLink   idConceptStart  4   NULL    13036   3.08    Using where
1   SIMPLE  link1   NULL    eq_ref  idx_name,idConceptStart,idConceptTarget,idConceptLink   idx_name    12  sandra.l.idConceptStart,const,const 1   90.00   Using where
1   SIMPLE  link2   NULL    eq_ref  idx_name,idConceptStart,idConceptTarget,idConceptLink   idx_name    12  sandra.l.idConceptStart,const,const 1   90.00   Using where

fastExplain

Редактировать 2

оптимальная таблица для сортировки кажется случайной.Теперь, когда я выполнил тот же запрос несколько часов спустя (произошла некоторая вставка), но с использованием того же запроса изменилась структура решения порядка ключей.Быстрый запрос становится медленным, а медленный - быстрым.Если я ORDER BY l.idConceptStart, то следующее объяснение

explain edit 2

порядок разрешения таблицы для ключей выглядит случайным.Я полностью потерян.В конце концов, единственное, что мне нужно, - это сначала получить последнюю запись в базе данных

Ответы [ 2 ]

1 голос
/ 14 июня 2019

"полностью проиндексирован" - Нет. У вас есть несколько индексов, включая некоторые избыточные.

Это может быть оптимальный индекс для вашего запроса:

INDEX(link, target, start)

Давайте поговорим оflag.Сколько у него разных значений?Если только 2 (скажем, 0 и 1), то измените на flag = 0 вместо flag != 1.Оптимизатор лучше работает с = тестами, чем !=.И изменить на INDEX(link, target, flag, start).

Какой процент строк имеет флаг = 1?Это может привести к дальнейшим размышлениям.

У вас есть ключ UNIQUE плюс суррогат id?Вы ссылаетесь на id из любой другой таблицы?Если нет, избавьтесь от него и продвигайте от UNIQUE до PRIMARY KEY.Но в этот момент я бы хотел, чтобы столбцы в этом ПК переставлялись в соответствии с моим предложением.

Некоторые правила построения индексов:

  • Сначала ставьте столбцы, проверенные с помощью =(link и target, в любом порядке)
  • Лучше иметь индекс полностью для обработки WHERE (!= останавливает это), если вы хотите еготакже включить столбцы ORDER BY.Это особенно верно, если есть также LIMIT.
  • UNIQUE(a,b,c) исключает необходимость в INDEX(a,b,c)
  • INDEX(a,b) исключает необходимость в INDEX(a).
  • Подробнее: http://mysql.rjweb.org/doc.php/index_cookbook_mysql
0 голосов
/ 13 июня 2019

Используя ЗАКАЗАТЬ ПО l.idConceptStart DESC скорее, чем ЗАКАЗАТЬ по ссылке link2.idConceptStart DESC вы смогли избежать, по-видимому, EXPLAIN временного хранилища и сортировки файлов и количество строк, к которым необходимо получить доступ, было значительно уменьшено.

Просмотр моего профиля, Сетевой профиль для контактной информации.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...