Производительность MYSQL медленная с использованием сортировки файлов

У меня есть простой запрос mysql, но когда у меня много записей (в настоящее время 103 0000), производительность очень низкая, и он говорит, что использует сортировку файлов, я не уверен, что именно поэтому он медленный. Есть ли у кого-нибудь предложения по ускорению? или остановить его с помощью сортировки файлов?

MYSQL-запрос:

SELECT adverts .*    
FROM adverts
WHERE (
price >='0'
)
AND (
adverts.status = 1
)
AND (
adverts.approved = 1
)
ORDER BY date_updated DESC 
LIMIT 19990 , 10

Результаты объяснения:

id   select_type   table   type    possible_keys    key    key_len    ref    rows   Extra 
1    SIMPLE        adverts range   price            price  4          NULL   103854 Using where; Using filesort

Вот таблица объявлений и индексы:

CREATE TABLE `adverts` (
  `advert_id` int(10) NOT NULL AUTO_INCREMENT,
  `user_id` int(10) NOT NULL,
  `type_id` tinyint(1) NOT NULL,
  `breed_id` int(10) NOT NULL,
  `advert_type` tinyint(1) NOT NULL,
  `headline` varchar(50) NOT NULL,
  `description` text NOT NULL,
  `price` int(4) NOT NULL,
  `postcode` varchar(7) NOT NULL,
  `town` varchar(60) NOT NULL,
  `county` varchar(60) NOT NULL,
  `latitude` float NOT NULL,
  `longitude` float NOT NULL,
  `telephone1` varchar(15) NOT NULL,
  `telephone2` varchar(15) NOT NULL,
  `email` varchar(80) NOT NULL,
  `status` tinyint(1) NOT NULL DEFAULT '0',
  `approved` tinyint(1) NOT NULL DEFAULT '0',
  `date_created` datetime NOT NULL,
  `date_updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `expiry_date` datetime NOT NULL,
  PRIMARY KEY (`advert_id`),
  KEY `price` (`price`),
  KEY `user` (`user_id`),
  KEY `type_breed` (`type_id`,`breed_id`),
  KEY `headline_keywords` (`headline`),
  KEY `date_updated` (`date_updated`),
  KEY `type_status_approved` (`advert_type`,`status`,`approved`)
) ENGINE=MyISAM AUTO_INCREMENT=103878 DEFAULT CHARSET=utf8

person user1052096    schedule 27.08.2012    source источник
comment
Это также медленно, если вы опустите предложение LIMIT 19990,10? Если вы отключите это для LIMIT 10, вы можете получить гораздо более быструю работу. В этом случае вы можете выяснить, как использовать SELECT, а не LIMIT для извлечения этих строк.   -  person O. Jones    schedule 28.08.2012
comment
Я просто добавил индекс status_approved (status,approved,date_updated). В объяснении теперь говорится, что он использует where вместо файловой сортировки, и запрос занимает 0,04 секунды вместо 2 секунд +, как это было раньше, я думаю, что это могло решить эту проблему.   -  person user1052096    schedule 28.08.2012
comment
Просто несвязанная мысль... ни одна из этих скобок не нужна.   -  person Dan Grossman    schedule 28.08.2012


Ответы (5)


Проблема в том, что MySQL использует только один индекс при выполнении запроса. Если вы добавите новый индекс, который использует 3 поля в предложении WHERE, строки будут найдены быстрее.

ALTER TABLE `adverts` ADD INDEX price_status_approved(`price`, `status`, `approved`);

Согласно документации MySQL ORDER BY Optimization:

В некоторых случаях MySQL не может использовать индексы для разрешения ORDER BY, хотя он по-прежнему использует индексы для поиска строк, соответствующих предложению WHERE. К таким случаям относятся следующие:
Ключ, используемый для выборки строк, не совпадает с ключом, используемым в ORDER BY.

Вот что происходит в вашем случае. Как сообщает нам вывод EXPLAIN, оптимизатор использует ключ price для поиска строк. Однако ORDER BY находится в поле date_updated, которое не принадлежит ключу price.

Чтобы быстрее находить строки И сортировать строки быстрее, вам нужно добавить индекс, который содержит все поля, используемые в предложениях WHERE и ORDER BY:

ALTER TABLE `adverts` ADD INDEX status_approved_date_updated(`status`, `approved`, `date_updated`);

Поле, используемое для сортировки, должно находиться на последней позиции в индексе. Бесполезно включать price в индекс, потому что условие, используемое в запросе, вернет диапазон значений.

Если EXPLAIN по-прежнему показывает, что он использует файловую сортировку, вы можете попробовать заставить MySQL использовать выбранный вами индекс:

SELECT adverts.*
FROM adverts
FORCE INDEX(status_approved_date_updated)
WHERE price >= 0
AND adverts.status = 1
AND adverts.approved = 1
ORDER BY date_updated DESC 
LIMIT 19990, 10

Обычно нет необходимости форсировать индекс, потому что оптимизатор MySQL чаще всего делает правильный выбор. Но иногда он делает плохой выбор или не самый лучший выбор. Вам нужно будет запустить несколько тестов, чтобы увидеть, улучшает ли это производительность или нет.

person Jocelyn    schedule 27.08.2012
comment
Я сделал это сейчас, спасибо, это не сильно ускорило его, и он все еще использует файловую сортировку. Есть ли способ использовать индекс вместо файловой сортировки? - person user1052096; 28.08.2012
comment
Основываясь на вашем выводе объяснения, он использует индекс цен. Я думаю, что ваша сортировка DESC вызывает часть проблемы. - person bobwienholt; 28.08.2012
comment
@user1052096 user1052096: можете ли вы опубликовать EXPLAIN вывод запроса после создания индекса в моем ответе? - person Jocelyn; 28.08.2012

Снимите галочки вокруг '0' - в настоящее время это может помешать использованию индекса, но я не уверен. Тем не менее, это лучший стиль, поскольку цена имеет тип int, а не символьный столбец.

SELECT adverts .*    
FROM adverts
WHERE (
price >= 0
)
AND (
adverts.status = 1
)
AND (
adverts.approved = 1
)
ORDER BY date_updated DESC 
LIMIT 19990 , 10
person Fabian Barney    schedule 27.08.2012

MySQL не использует ключ date_updated для сортировки, а просто использует ключ price, поскольку он используется в предложении WHERE. Вы можете попробовать использовать подсказки индекса:

http://dev.mysql.com/doc/refman/5.1/en/index-hints.html

Добавьте что-то вроде

USE KEY FOR ORDER BY  (date_updated)
person hol    schedule 27.08.2012

У меня есть два предложения. Во-первых, удалите кавычки вокруг нуля в предложении where. Эта строка должна быть:

price >= 0

Во-вторых, создайте этот индекс:

CREATE INDEX `helper` ON `adverts`(`status`,`approved`,`price`,`date_created`);

Это должно позволить MySQL найти 10 строк, указанных в вашем предложении LIMIT, используя только индекс. Сортировка файлов сама по себе неплоха... количество строк, которые необходимо обработать.

person bobwienholt    schedule 27.08.2012

Ваше условие WHERE использует price, status, approved для выбора, а затем date_updated используется для сортировки.

Поэтому вам нужен единый индекс с этими полями; Я предлагаю индексировать approved, status, price и date_updated именно в таком порядке.

Общее правило заключается в том, что первыми ставятся равенства WHERE, затем диапазоны (больше, меньше или равно, между и т. д.), а поля сортировки — последними. (Обратите внимание, что отсутствие одного поля может сделать индекс менее пригодным или даже непригодным для этой цели).

CREATE INDEX advert_ndx ON adverts (approved, status, price, date_updated);

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

Я бы также удалил все ненужные индексы, что ускорило бы INSERTs и UPDATEs.

person LSerni    schedule 27.08.2012