Исключение значений из одной таблицы с другой. Супер медленный - PullRequest
0 голосов
/ 20 октября 2011

В той же базе данных у меня есть таблица messages, чьи столбцы: id, title, text Я хочу.Я хочу, чтобы только записи, в которых title не имеет записей в таблице lastlogon, чей эквивалент названия затем называется username.

Я использовал эту команду SQL в PHP, обычно это занимало 2-3 секунды, чтобы подтянуть:

SELECT DISTINCT * FROM messages WHERE title NOT IN (SELECT username FROM lastlogon) LIMIT 1000

Все это было хорошо, пока таблица lastlogon не начала иметь около 80% таблицы значений messages.Сообщения содержат около 8000 записей, последний - около 7000. Теперь для их прохождения требуется от минуты до двух минут.MySQL работает с очень высокой загрузкой ЦП.

Я попробовал следующее, но безуспешно сократил время:

SELECT id,title,text FROM messages a LEFT OUTER JOIN lastlogon b ON (a.title = b.username) LIMIT 1000

Почему вдруг это требуеттак долго для такого низкого количества записей?Я попытался перезапустить MySQL и Apache несколько раз.Я использую Debian Linux.

Редактировать: Вот структуры

--
-- Table structure for table `lastlogon`
--

CREATE TABLE IF NOT EXISTS `lastlogon` (
  `username` varchar(25) NOT NULL,
  `lastlogon` date NOT NULL,
  `datechecked` date NOT NULL,
  PRIMARY KEY (`username`),
  KEY `username` (`username`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

-- --------------------------------------------------------

--
-- Table structure for table `messages`
--

CREATE TABLE IF NOT EXISTS `messages` (
  `id` smallint(9) unsigned NOT NULL AUTO_INCREMENT,
  `title` varchar(255) NOT NULL,
  `name` varchar(255) NOT NULL,
  `email` varchar(50) NOT NULL,
  `text` mediumtext,
  `folder` tinyint(2) NOT NULL,
  `read` smallint(5) unsigned NOT NULL,
  `dateline` int(10) unsigned NOT NULL,
  `ip` varchar(15) NOT NULL,
  `attachment` varchar(255) NOT NULL,
  `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `username` varchar(300) NOT NULL,
  `error` varchar(500) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `title` (`title`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=9010 ;

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

Отредактированная структура с новыми индексами.После добавления индекса для messages.title и lastlogon.username я получил следующие результаты:

Отображение строк 0–29 (всего 623, запрос занимал 74,4938 с)

Ответы [ 2 ]

1 голос
/ 20 октября 2011

Первый: замените ключ в заголовке составным ключом в заголовке + id

ALTER TABLE messages DROP INDEX title;
ALTER TABLE messages ADD INDEX title (title, id);

Теперь измените выбор на:

SELECT m.* FROM messages m
LEFT JOIN lastlogon l ON (l.username = m.title)
WHERE l.username IS NULL
-- GROUP BY m.id DESC -- faster replacement for distinct. I don't think you need this.
LIMIT 1000;

Или

SELECT m.* FROM messages m
WHERE m.title NOT IN (SELECT l.username FROM lastlogon l)
-- GROUP BY m.id DESC -- faster than distinct, I don't think you need it though.
LIMIT 1000;

Другая проблема с медлительностью - это SELECT m.* деталь.
Выбрав все столбцы, вы заставляете MySQL выполнять дополнительную работу.
Выбирайте только те столбцы, которые вам нужны:

SELECT m.title, m.name, m.email, ......

Это также ускорит запрос.

Есть еще один прием, который вы можете использовать:
Замените лимит 1000 датой отсечения.

Шаг 1: Добавьте индекс на метку времени (или любое другое поле, которое вы хотите использовать для отсечки).

SELECT m.* FROM messages m
LEFT JOIN lastlogon l ON (l.username = m.title)
WHERE (m.id > (SELECT MIN(M2.ID) FROM messages m2 WHERE m2.timestamp >= '2011-09-01'))
  AND l.username IS NULL
-- GROUP BY m.id DESC -- faster replacement for distinct. I don't think you need this.
0 голосов
/ 20 октября 2011

Я предлагаю вам добавить индекс на messages.title.Затем попробуйте снова выполнить запрос и проверить производительность.

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