Супер медленный запрос с CROSS JOIN - PullRequest
2 голосов
/ 04 июля 2011

У меня есть две таблицы с именем table_1 (1 ГБ) и ссылка (250 МБ).

Когда я запрашиваю перекрестное соединение по ссылке, для обновления таблицы_1 требуется 16 часов. Мы изменили системные файлы EXT3 для XFS, но все равно это заняло 16 часов. ЧТО Я ДЕЛАЮ НЕПРАВИЛЬНО ??Вот запрос на обновление / перекрестное объединение:

  mysql> UPDATE table_1 CROSS JOIN reference ON
  -> (table_1.start >= reference.txStart AND table_1.end <= reference.txEnd)
  -> SET table_1.name = reference.name;
  Query OK, 17311434 rows affected (16 hours 36 min 48.62 sec)
  Rows matched: 17311434  Changed: 17311434  Warnings: 0

Вот таблица создания show_1 таблицы и ссылки:

    CREATE TABLE `table_1` (
     `strand` char(1) DEFAULT NULL,
     `chr` varchar(10) DEFAULT NULL,
     `start` int(11) DEFAULT NULL,
     `end` int(11) DEFAULT NULL,
     `name` varchar(255) DEFAULT NULL,
     `name2` varchar(255) DEFAULT NULL,
     KEY `annot` (`start`,`end`)
   ) ENGINE=MyISAM DEFAULT CHARSET=latin1 ;


   CREATE TABLE `reference` (
     `bin` smallint(5) unsigned NOT NULL,
     `name` varchar(255) NOT NULL,
     `chrom` varchar(255) NOT NULL,
     `strand` char(1) NOT NULL,
     `txStart` int(10) unsigned NOT NULL,
     `txEnd` int(10) unsigned NOT NULL,
     `cdsStart` int(10) unsigned NOT NULL,
     `cdsEnd` int(10) unsigned NOT NULL,
     `exonCount` int(10) unsigned NOT NULL,
     `exonStarts` longblob NOT NULL,
     `exonEnds` longblob NOT NULL,
     `score` int(11) DEFAULT NULL,
     `name2` varchar(255) NOT NULL,
     `cdsStartStat` enum('none','unk','incmpl','cmpl') NOT NULL,
     `cdsEndStat` enum('none','unk','incmpl','cmpl') NOT NULL,
     `exonFrames` longblob NOT NULL,
      KEY `chrom` (`chrom`,`bin`),
      KEY `name` (`name`),
      KEY `name2` (`name2`),
      KEY `annot` (`txStart`,`txEnd`)
   ) ENGINE=MyISAM DEFAULT CHARSET=latin1 ;

Ответы [ 5 ]

4 голосов
/ 04 июля 2011

Вы должны индексировать table_1.start, reference.txStart, table_1.end и reference.txEnd поля таблицы:

ALTER TABLE `table_1` ADD INDEX ( `start` ) ;
ALTER TABLE `table_1` ADD INDEX ( `end` ) ;
ALTER TABLE `reference` ADD INDEX ( `txStart` ) ;
ALTER TABLE `reference` ADD INDEX ( `txEnd` ) ;
1 голос
/ 04 июля 2011

Перекрестные объединения - это декартовы произведения, которые, вероятно, являются одними из самых дорогих в вычислительном отношении вычислений (они плохо масштабируются).

Для каждой таблицы T_i для i = от 1 до n числострок, сгенерированных путем пересечения таблиц T_1-T_n - это размер каждой таблицы, умноженный на размер каждой другой таблицы, то есть

| T_1 |* | T_2 |* ... * | T_n |

Если предположить, что в каждой таблице имеется по M строк, итоговая стоимость вычисления перекрестного соединения составит

M_1 * M_2 ... M_n = O (M ^ n)

, который является экспоненциальным по количеству таблиц, участвующих в объединении.

0 голосов
/ 06 июля 2011

Кто-то уже предлагал вам добавить несколько индексов. Но я думаю, что лучшую производительность вы можете получить с этими двумя показателями:

ALTER TABLE `test`.`time` 
    ADD INDEX `reference_start_end` (`txStart` ASC, `txEnd` ASC),
    ADD INDEX `table_1_star_end` (`start` ASC, `end` ASC);

Только один из них будет использоваться запросом MySQL, но MySQL сам решит, что будет более полезным.

0 голосов
/ 04 июля 2011

Я вижу 2 проблемы с оператором UPDATE.

Нет индекса для полей End. Составные индексы (annot), которые вы используете, будут использоваться только для полей start в этом запросе. Вы должны добавить их в соответствии с предложением Эмре:

ALTER TABLE `table_1` ADD INDEX ( `end` ) ;
ALTER TABLE `reference` ADD INDEX ( `txEnd` ) ;

Во-вторых, JOIN может (и, вероятно, делает) найти много строк таблицы reference, которые связаны со строкой table_1. Таким образом, некоторые (или все) строки table_1, которые обновляются, обновляются много раз. Проверьте результат этого запроса, чтобы увидеть, совпадает ли он с вашим обновленным количеством строк (17311434):

SELECT COUNT(*)
FROM table_1
  WHERE EXISTS
    ( SELECT *
      FROM reference
      WHERE table_1.start >= reference.txStart
        AND table_1.`end` <= reference.txEnd
    )

Могут быть другие способы написания этого запроса, но отсутствие PRIMARY KEY в обеих таблицах усложняет его. Если вы определяете первичный ключ для table_1, попробуйте это, заменив id на первичный ключ.

Обновление : Нет, не пробуйте его на таблице с 34M строками. Проверьте план выполнения и попробуйте сначала с меньшими таблицами.

UPDATE table_1 AS t1
  JOIN 
    ( SELECT t2.id
           , r.name
      FROM table_1 AS t2
        JOIN
          ( SELECT name, txStart, txEnd
            FROM reference
            GROUP BY txStart, txEnd
          ) AS r
          ON  t2.start >= r.txStart
          AND t2.`end` <= r.txEnd
      GROUP BY t2.id
    ) AS good
    ON good.id = t1.id
SET t1.name = good.name;

План запроса можно проверить, запустив EXPLAIN для эквивалентного SELECT:

EXPLAIN
SELECT t1.id, t1.name, good.name
FROM table_1 AS t1
  JOIN 
    ( SELECT t2.id
           , r.name
      FROM table_1 AS t2
        JOIN
          ( SELECT name, txStart, txEnd
            FROM reference
            GROUP BY txStart, txEnd
          ) AS r
          ON  t2.start >= r.txStart
          AND t2.`end` <= r.txEnd
      GROUP BY t2.id
    ) AS good
    ON good.id = t1.id ;
0 голосов
/ 04 июля 2011

Попробуйте это:

UPDATE table_1 SET
table_1.name = (
  select reference.name
  from reference
  where table_1.start >= reference.txStart
  and table_1.end <= reference.txEnd)
...