SQLinfo.ru - Все о MySQL Webew.ru: теория и практика веб-технологий

Форум пользователей MySQL

Задавайте вопросы, мы ответим

Вы не зашли.

#1 13.04.2011 12:27:24

straylon
Участник
Зарегистрирован: 13.04.2011
Сообщений: 23

Помогите оптимизировать запрос SELECT на выборку элементов

Здравствуйте! Помогите, пожалуйста, решить следующую проблему. Есть таблица с ~ 10 млн. записей:
CREATE TABLE  `indexdata`.`word_weight` (
  `DocID` mediumint(8) unsigned NOT NULL DEFAULT '0',
  `WordID` mediumint(8) unsigned NOT NULL DEFAULT '0',
  `Weight` float(11,7) DEFAULT NULL,
  `WeigtID` int(20) unsigned NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`WeigtID`),
  UNIQUE KEY `Index_2` (`WordID`,`DocID`) USING BTREE,
  UNIQUE KEY `Index_3` (`DocID`,`WordID`)
) ENGINE=MyISAM AUTO_INCREMENT=8104793 DEFAULT CHARSET=utf8;

Необходимо делать из нее выборку DocID, в которых встречаются все слова с определенными WordID (их задает пользователь).
Делаю это следующим образом (запрос генерирую программно, ниже представлен вариант запроса для четырех слов):

SELECT DocID, SUM(Weight) FROM indexdata.word_weight WHERE
WordID = 69690 OR WordID = 204534 OR WordID = 22544 OR WordID = 46839 GROUP BY DocID having count(*) > 3
(SUM(Weight) - сумма весов для данных слов).

Время выполнения порядка 4-5 секунд, что не устраивает (компьютер RAM 2 Гб, CPU Intel 2 Ггц). Основное время уходит на Copy data to temporary table (tmp_table_size = 200M).
Можно ли как-нибудь изменить данный запрос или его оптимизировать, чтобы время выполнения было на уровне 1 с? Заранее благодарен за помощь. P.S. Explain данного запроса приложен ниже.


Прикрепленные файлы:
Attachment Icon 1.JPG, Размер: 38,584 байт, Скачано: 564

Неактивен

 

#2 13.04.2011 13:36:58

LazY
_cмельчак
MySQL Authorized Developer and DBA
Зарегистрирован: 02.04.2007
Сообщений: 849

Re: Помогите оптимизировать запрос SELECT на выборку элементов

Индекс используется правильный. Судя по всему, основное время занимает лазание по диску за колонкой Weight.
Попробуйте дотянуть индекс до Weight: KEY(WordID, DocID, Weight).

Неактивен

 

#3 13.04.2011 13:49:47

straylon
Участник
Зарегистрирован: 13.04.2011
Сообщений: 23

Re: Помогите оптимизировать запрос SELECT на выборку элементов

Убрал Sum(Weight) вообще из запроса. Скорость немного улучшилась, но все равно на уровне 3 секунд...

Неактивен

 

#4 13.04.2011 14:06:30

LazY
_cмельчак
MySQL Authorized Developer and DBA
Зарегистрирован: 02.04.2007
Сообщений: 849

Re: Помогите оптимизировать запрос SELECT на выборку элементов

А что показывает EXPLAIN для запроса без SUM(Weight)?

Неактивен

 

#5 13.04.2011 14:20:04

straylon
Участник
Зарегистрирован: 13.04.2011
Сообщений: 23

Re: Помогите оптимизировать запрос SELECT на выборку элементов

Тоже самое, что и с SUM(Weight)

Неактивен

 

#6 13.04.2011 14:41:47

LazY
_cмельчак
MySQL Authorized Developer and DBA
Зарегистрирован: 02.04.2007
Сообщений: 849

Re: Помогите оптимизировать запрос SELECT на выборку элементов

Что показывает EXPLAIN для следующих двух запросов:


SELECT WordID, DocID
FROM indexdata
WHERE WordID = ... OR WordID = ...
GROUP BY WordID, DocID


SELECT WordID, DocID
FROM indexdata
WHERE WordID = ... OR WordID = ...
GROUP BY WordID, DocID
HAVING COUNT(*) > 3

?

Неактивен

 

#7 13.04.2011 15:03:14

straylon
Участник
Зарегистрирован: 13.04.2011
Сообщений: 23

Re: Помогите оптимизировать запрос SELECT на выборку элементов

Прогнал через explain. Результаты приложил. Как я понял, сейчас MySQL не использует temporary table. Протестирую по быстродействию. Результаты сообщу. Может и быстрее будет.


Прикрепленные файлы:
Attachment Icon 2.JPG, Размер: 33,786 байт, Скачано: 473

Неактивен

 

#8 13.04.2011 15:04:39

straylon
Участник
Зарегистрирован: 13.04.2011
Сообщений: 23

Re: Помогите оптимизировать запрос SELECT на выборку элементов

И explain для второго запроса.


Прикрепленные файлы:
Attachment Icon 3.JPG, Размер: 38,562 байт, Скачано: 569

Неактивен

 

#9 13.04.2011 15:43:04

LazY
_cмельчак
MySQL Authorized Developer and DBA
Зарегистрирован: 02.04.2007
Сообщений: 849

Re: Помогите оптимизировать запрос SELECT на выборку элементов

Да, теперь индекс используется. Это достигнуто искусственным включением его первой части (WordID) в GROUP BY.

Следующий шаг - включить в индекс колонку Weight (чтобы и за Weight не приходилось идти на диск) и попробовать ваш самый первый запрос.

Неактивен

 

#10 13.04.2011 18:04:04

straylon
Участник
Зарегистрирован: 13.04.2011
Сообщений: 23

Re: Помогите оптимизировать запрос SELECT на выборку элементов

LazY, протестировал такого вида запросы, но они работать не будут, т.к. идет упорядочивание по парам WordID и DocID и при любом значении having count(*) > отличным от нуля, будут возвращать пустой результат (т.к. DocID повторяется в таблице, а WordID относительно DocID уникально). Может еще какие-соображения есть?

Неактивен

 

#11 13.04.2011 19:52:49

LazY
_cмельчак
MySQL Authorized Developer and DBA
Зарегистрирован: 02.04.2007
Сообщений: 849

Re: Помогите оптимизировать запрос SELECT на выборку элементов

А что показывает EXPLAIN SELECT WordID, DocID FROM index WHERE Wordid = ... OR ... — без GROUP BY?

И сколько такой запрос выполняется?

Неактивен

 

#12 14.04.2011 10:53:42

straylon
Участник
Зарегистрирован: 13.04.2011
Сообщений: 23

Re: Помогите оптимизировать запрос SELECT на выборку элементов

Explain запроса приложил. Выполняется ~ 3.5 c. Особо выигрыша не дает. Может условие через OR для MySQL сложно выполнить?


Прикрепленные файлы:
Attachment Icon 4.JPG, Размер: 34,992 байт, Скачано: 526

Неактивен

 

#13 14.04.2011 11:35:37

LazY
_cмельчак
MySQL Authorized Developer and DBA
Зарегистрирован: 02.04.2007
Сообщений: 849

Re: Помогите оптимизировать запрос SELECT на выборку элементов

Нет, OR в данном случае — это все равно, что IN, т.е. выборка RANGE по ключу.

EXPLAIN у вас правильный и хороший: Using where, Using index. Второе означает, что MySQL вообще не лезет на диск, и 3.5 секунды занимает операция с индексом. Это необычно медленно. Скорее всего, индекс у вас слишком большой и не помещается в key_buffer.

Будьте добры SHOW TABLE STATUS LIKE 'word_weight' и SHOW VARIABLES LIKE 'key_buffer%'

Если есть возможность - выполните эти запросы в консоли mysql с \G на конце. Т.е., например,

SHOW TABLE STATUS LIKE 'word_weight'\G

Так будет удобнее читать (да и писать) и не потребуется загружать картинки.

Неактивен

 

#14 14.04.2011 11:47:13

straylon
Участник
Зарегистрирован: 13.04.2011
Сообщений: 23

Re: Помогите оптимизировать запрос SELECT на выборку элементов

Приложил интересующие Вас запросы. Сравнил параметры. Вроде значения key_buffer хватает, но все таки есть индексы и в других таблицах. Не подскажите, каким оператором загрузить весь индекс из таблицы word_weight в память?


Прикрепленные файлы:
Attachment Icon 5.JPG, Размер: 82,421 байт, Скачано: 544

Неактивен

 

#15 14.04.2011 11:53:41

straylon
Участник
Зарегистрирован: 13.04.2011
Сообщений: 23

Re: Помогите оптимизировать запрос SELECT на выборку элементов

Я так понимаю, что если такого вида запрос будет быстро выполняться, то мне придеться его программно обрабатывать вместо GROUP BY ... HAVING()...

Неактивен

 

#16 14.04.2011 12:33:41

LazY
_cмельчак
MySQL Authorized Developer and DBA
Зарегистрирован: 02.04.2007
Сообщений: 849

Re: Помогите оптимизировать запрос SELECT на выборку элементов

Не подскажите, каким оператором загрузить весь индекс из таблицы word_weight в память?

Вообще это делается командой LOAD INDEX INTO CACHE:

LOAD INDEX INTO CACHE word_weight INDEX (index_2)

Если опустить часть INDEX (..), будут загружены все индексы таблицы.


Вообще говоря, есть возможность выделить конкретной таблице отдельный буфер ключей, которым будет пользоваться только эта таблица, а другие не будут её оттуда вытеснять. Однако для этого придется потрудиться.


Прежде всего, нужно будет выполнить две команды:

SET GLOBAL word_weight_keycache.key_buffer_size = 300*1024*1024;

(запись вида что-то.key_buffer_size указывает на буфер ключей с именем 'что-то', устанавливая его значение в байтах; см. http://dev.mysql.com/doc/refman/5.1/en/ … aches.html).

И вторая команда:

CACHE INDEX word_weight INDEX (index_2) IN word_weight_keycache;

С этого момента индекс index_2 таблицы word_weight будет пользоваться специальным буфером (если нужно сделать это для всех индексов таблицы — стоит опустить часть INDEX (...); подробнее см. описание команды CACHE INDEX).
Индекс будет автоматически загружен в буфер при первом обращении к нему. Чтобы не дожидаться этого, можно загрузить индекс в буфер сразу:

LOAD INDEX INTO CACHE word_weight INDEX (index_2)


Только вот есть одна проблема: при перезапуске сервера эти установки не сохраняются, и если вы собираетесь пользоваться ими все время, нужно сделать еще две вещи:

1. В секции [mysqld] конфигурационного файла MySQL нужно добавить установку вашего буфера ключей:

word_weight_keycache.key_buffer_size = 300M


2. Нужно создать sql-файл, который будет выполняться каждый раз при запуске сервера, который должен содержать вышеуказанную команду CACHE INDEX. Это делается с помощью директивы init_file в секции [mysqld]. Например,

[mysqld]
...
init_file=/var/lib/mysql/start.sql
...

В нём же, при желании, можно указать сразу и LOAD INDEX.


Я так понимаю, что если такого вида запрос будет быстро выполняться, то мне придеться его программно обрабатывать вместо GROUP BY ... HAVING()...

Не факт. До конца станет понятно на практике, когда, ускорив этот запрос, вы перейдёте к запросу с GROUP BY и HAVING(). По идее, выполняться он должен примерно с той же скоростью, как и тот, что вы сейчас тестируете, т.к. тут лимитирующая стадия — поход за данными, а данные им обоим нужны одни и те же.
Попробуйте — а там посмотрим.

Неактивен

 

#17 14.04.2011 13:01:28

straylon
Участник
Зарегистрирован: 13.04.2011
Сообщений: 23

Re: Помогите оптимизировать запрос SELECT на выборку элементов

LaZy, ну прямо чудеса. Решил пока sql файл не делать. Выполнил следующее. Закинул в память все индексы из word_weight посредством запросов

SET GLOBAL keycache1.key_buffer_size = 300*1024*1024;
CACHE INDEX indexdata.word_weight IN keycache1;
LOAD INDEX INTO CACHE indexdata.word_weight;

Выполнил запрос

SELECT word_weight.DocID
FROM indexdata.word_weight
WHERE WordID = 892 OR WordID = 23939 OR WordID = 53020 OR WordID = 86842
OR WordID = 31615 OR WordID = 22544 OR WordID = 12707
OR WordID = 68131;

Explain: Index: Index_2; Rows: 38695; Extra: Using where; Using index.

Время выполнения: 7,6 секунд !!!!!!

Уже потерял надежду на оптимизацию ;-)

Неактивен

 

#18 14.04.2011 13:41:32

LazY
_cмельчак
MySQL Authorized Developer and DBA
Зарегистрирован: 02.04.2007
Сообщений: 849

Re: Помогите оптимизировать запрос SELECT на выборку элементов

Да, действительно очень странно.

Скажите, а как вы измеряете время выполнения запроса?

Насколько нагружена система в остальном? Не занят ли процессор?

Неактивен

 

#19 14.04.2011 13:55:52

straylon
Участник
Зарегистрирован: 13.04.2011
Сообщений: 23

Re: Помогите оптимизировать запрос SELECT на выборку элементов

LaZy, для чистоты эксперимента поступаю следующим образом:
перезагружаю компьютер, загружаю индексы в память. В окне MySQL Query Browser выполняю запрос. Время пишет после выполнения запроса там же. На компьютере в этот момент ничего не выполняется. Процессор процентов на 80 занят mysqld. Вот такие дела.

Неактивен

 

#20 14.04.2011 13:58:05

straylon
Участник
Зарегистрирован: 13.04.2011
Сообщений: 23

Re: Помогите оптимизировать запрос SELECT на выборку элементов

После этого тестирую на разные запросы с различным количеством WordID. Время выполнения от 2 до 4 секунд в зависимости от количества условий OR.

Неактивен

 

#21 14.04.2011 14:07:57

LazY
_cмельчак
MySQL Authorized Developer and DBA
Зарегистрирован: 02.04.2007
Сообщений: 849

Re: Помогите оптимизировать запрос SELECT на выборку элементов

MySQL Query Browser может тратить много времени на построение отображения запроса.
Попробуйте запустить клиент mysql и посмотреть время там.

Если время и там медленное — давайте попробуем детально посмотреть на этапы выполнения запроса. Для этого выполните его еще раз, а потом сразу после него выполните запрос SHOW PROFILE (см. http://webew.ru/articles/2732.webew).

Неактивен

 

#22 14.04.2011 14:08:35

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6757

Re: Помогите оптимизировать запрос SELECT на выборку элементов

4 тысячи строк в секунду. Мне кажется, Вы упираетесь не в MySQL, а в клиентское
приложение, которое не успевает эти строки читать wink

Попробуйте аналогичный SELECT COUNT(*) — запрос должен выполняться моментально.

Неактивен

 

#23 14.04.2011 14:16:42

straylon
Участник
Зарегистрирован: 13.04.2011
Сообщений: 23

Re: Помогите оптимизировать запрос SELECT на выборку элементов

Запрос
SELECT count(*)
FROM indexdata.word_weight WHERE
WordID = 26570 OR WordID = 74843 OR WordID = 16347 OR WordID = 892;
Результат 11077 строк
1 row fetched in 0,0010 s (1.1337 s);
Ощутимо, что выполнялся запрос ~ 1 s. Неясно, что за цифра 0,0010 s?

Неактивен

 

#24 14.04.2011 14:25:28

straylon
Участник
Зарегистрирован: 13.04.2011
Сообщений: 23

Re: Помогите оптимизировать запрос SELECT на выборку элементов

Кстати, посмотрел что пишет в State запроса - основное время тратится на Sending Data

Неактивен

 

#25 14.04.2011 14:29:24

LazY
_cмельчак
MySQL Authorized Developer and DBA
Зарегистрирован: 02.04.2007
Сообщений: 849

Re: Помогите оптимизировать запрос SELECT на выборку элементов

основное время тратится на Sending Data

Вот!
Это говорит о том, что вы упираетесь в приложение, которое медленно принимает данные, а не в производительность MySQL.

Если в вашем рабочем приложении вы эти данные выводите человеку, вряд ли ему нужны все 20 000 записей, поэтому у вас наверняка будет LIMIT и клиентскому приложению станет полегче.

Неактивен

 

Board footer

Работает на PunBB
© Copyright 2002–2008 Rickard Andersson