Попытка оптимизировать MySQL-запрос с помощью LEFT OUTER JOIN - PullRequest
3 голосов
/ 26 марта 2011

У меня есть этот запрос, который отлично работает, за исключением того, что он занимает много времени (7 секунд, с 40k записей в таблице заданий и 700k в таблице wq).

Я попробовал EXPLAIN, и он говорит, что просматривает все записи в таблице заданий и не использует никаких индексов.

Я не знаю, как сказать MySQL, что ему следует использовать поле jobs.status для фильтрации записей перед поиском таблицы wq.

Цель этого - получить все записи из заданий, которые имеют статус! = 331, а также из любой другой работы, которая имеет статус wq (101, 111, 151).

Запрос:

SELECT jobs.*
FROM jobs
LEFT OUTER JOIN wq ON (wq.job = jobs.id AND jobs.status IN (341, 331) AND wq.status IN (101, 111, 151))
WHERE ((wq.info is not NULL) or (jobs.status != 331 and ack = 0))

ОБЪЯСНИТЕ вывод:

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
1   SIMPLE  jobs    ALL     ack,status,status_ack   NULL    NULL    NULL    38111   Using filesort
1   SIMPLE  wq  ref     PRIMARY,job,status  PRIMARY     4   cts.jobs.id     20  Using where

Таблица определений:

CREATE TABLE jobs ( id int(10) NOT NULL AUTO_INCREMENT,
comment varchar(100) NOT NULL DEFAULT '',
profile varchar(60) NOT NULL DEFAULT '',
start_at int(10) NOT NULL DEFAULT '0',
data text NOT NULL,
status int(10) NOT NULL DEFAULT '0',
info varchar(200) NOT NULL DEFAULT '',
finish int(10) NOT NULL DEFAULT '0',
priority int(5) NOT NULL DEFAULT '0',
ack tinyint(4) NOT NULL DEFAULT '0',
PRIMARY KEY (id),
KEY start_at (start_at),
KEY status (status),
KEY status_ack (status,
ack) ) ENGINE=MyISAM AUTO_INCREMENT=2037530 DEFAULT CHARSET=latin1;


CREATE TABLE wq ( job int(10) NOT NULL DEFAULT '0',
process varchar(60) NOT NULL DEFAULT '',
step varchar(60) NOT NULL DEFAULT '',
status int(10) NOT NULL DEFAULT '0',
run_at int(10) NOT NULL DEFAULT '0',
original_run_at int(10) NOT NULL DEFAULT '0',
info varchar(200) NOT NULL DEFAULT '',
pos int(10) NOT NULL DEFAULT '0',
changed_at int(10) NOT NULL DEFAULT '0',
file varchar(60) NOT NULL DEFAULT '',
PRIMARY KEY (job,
process,
step,
file),
KEY job (job),
KEY status (status) ) ENGINE=MyISAM DEFAULT CHARSET=latin1

1 Ответ

5 голосов
/ 26 марта 2011

К сожалению, mysql (и, возможно, любые dbms) не могут оптимизировать выражения типа jobs.status != 331 and ack = 0, потому что B-Tree не является структурой, которая позволяет быстро находить все, что не равно константе-значению. Таким образом, вы всегда получите полное сканирование.

Если бы было какое-то лучшее состояние, например jobs.status = 331 and ack = 0 (обратите внимание на тот факт, что я изменил != на =), то был бы совет ускорить этот запрос:

  1. разбить запрос на 2, к которым присоединяется UNION ALL
  2. заменить в одном запросе LEFT JOIN на INNER JOIN (в том, что подразумевает, что wq.info is not NULL)
...