Медленный MySQL запрос. Что я должен индексировать? - PullRequest
1 голос
/ 04 февраля 2009

PHPWiki имеет 5-секундный медленный запрос каждый раз, когда вы сохраняете редактирование страницы. Запрос, часто встречающийся в «mysql-slow.log»:

INSERT INTO wikiscore 
SELECT w1.topage, COUNT(*) 
FROM wikilinks AS w1, wikilinks AS w2 
WHERE w2.topage=w1.frompage 
GROUP BY w1.topage;

Текущие индексы следующие:

table "wikilinks" has a primary index on "frompage" and "topage" 
table "wikiscore" has a primary index on "pagename" and "score"

Как можно переформулировать запрос SELECT, чтобы быстрее возвращать те же результаты? Как я могу изменить индексы, чтобы этот запрос был быстрее? Я думаю, что это может быть переиндексировано?

Я рассчитал результат только для части запроса SELECT, и она занимает 1-2 секунды. ВСТАВКА должна занять оставшееся время.

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

Есть идеи?

редактировать ---

Результаты «EXPLAIN» в части запроса SELECT были:

SIMPLE
w2
index
PRIMARY
204
31871   
Using index; Using temporary; Using filesort

SIMPLE
w1
ref
PRIMARY
PRIMARY
102 
phpwiki.w2.topage   
14
Using index

Ответы [ 5 ]

3 голосов
/ 04 февраля 2009

таблица "wikilinks" имеет первичный индекс для "frompage" и "topage"

WHERE w2.topage=w1.frompage

Это условие не может быть найдено по составному индексу, описанному выше.

Либо измените порядок (создайте индекс на topage, frompage), либо создайте дополнительный индекс на topage.

P. S. Корень их проблемы в том, что ранги каждой и каждой страницы в системе обновляются при каждом редактировании.

Эта система ранжирования кажется мне несколько странной: она учитывает ссылки на ссылки, а не сами ссылки.

Если 1000 страниц ссылаются на Москва и только Москва ссылается на Бекет-пруд , то пруд получит 1000 очков и Москва вообще не получит очков, хотя все знают о Москве и о пруду.

Я думаю, это не то, что вы имели в виду. Скорее всего, это должно выглядеть так:

INSERT INTO
       wikiscore 
SELECT
       linked.topage, COUNT(*) AS cnt
FROM   wikilinks current, wikilinks linked
WHERE  current.frompage=@current_page
       AND linked.topage = current.topage
GROUP BY
       linked.topage
ON DUPLICATE KEY UPDATE
       score = cnt;

Суммирует все ссылки на все страницы, на которые ссылается текущая страница, и это, похоже, то, что вам нужно.

В этом случае вам нужно будет избавиться от score в PRIMARY KEY на wikiscore, но я все равно не вижу смысла ставить его там.

Если вы хотите ускорить ранжирование запросов, вы создаете такие индексы:

ALTER TABLE wikilinks ADD CONSTRAINT pk_wikilinkes_fromto PRIMARY KEY (frompage, topage);

CREATE INDEX ix_wikilinks_topage ON wikilinks (topage);

ALTER TABLE wikiscore ADD CONSTRAINT pk_wikiscore_pagename PRIMARY KEY (pagename);

CREATE INDEX ix_wikiscore_score ON wikiscore (score);
2 голосов
/ 04 февраля 2009

Полезно использовать оператор EXPLAIN , чтобы выяснить, какая часть вашего запроса занимает больше всего времени. Затем вы можете решить, какие меры следует предпринять для оптимизации вашего запроса.

1 голос
/ 04 февраля 2009

Ответ Quassnoi даст вам некоторую скорость в SELECT. Если INSERT занимает еще четыре секунды, то добавление индексов ничего не поможет. Возможно, вы могли бы исключить много данных из процесса, добавив AND COUNT (*)> 0 к вашему SELECT, если желательно пропустить страницы с нулевым числом входящих ссылок.

Вы можете получить хоть какое-то улучшение, удалив индексы из викискора. Ваш первичный ключ в pagename, оценка не имеет особого смысла (вы можете хранить несколько баллов на одной странице, но не в том случае, если они являются одинаковыми баллами?), И, вероятно, должны быть просто первичным ключом на имя страницы. Если есть другие индексы, вы можете от них избавиться.

Если викискор не был заново создан, когда это произойдет, вы можете получить некоторую выгоду от использования ОПТИМИЗИРУЮЩЕГО СТОЛА.

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

1 голос
/ 04 февраля 2009

У меня небольшие проблемы с пониманием того, что делает запрос. Я понимаю, что он находит ссылки с одной страницы на другую. поэтому w1.topage - это ссылки на эту страницу, а w1.frompage - это ссылки с этой страницы на другие страницы. И поэтому вставка добавляет страницу и количество ссылок на эту страницу.

Я на ходу?

Ваша главная проблема в этой строке:

FROM wikilinks AS w1, wikilinks AS w2 

Если вы предполагаете, что таблица имеет 1000 записей, обработчик запросов должен сопоставить 1000 записей друг с другом, поэтому он захватывает 1000 и 1000 раз (без учета предложения WHERE или GROUP). По мере того, как вы получаете все больше и больше записей, время запроса увеличивается в геометрической прогрессии. (Kaboom)

Кроме того, вы редактируете только одну страницу, поэтому вы должны разумно предположить, что ссылки на эту конкретную страницу не изменятся, но ссылки на нее могут измениться. Поэтому вместо записи таблицы wikilinks при каждом обновлении удаляйте ссылки с этой конкретной страницы, а затем повторно вставляйте все ссылки с этой страницы на другие.

0 голосов
/ 05 февраля 2009

Вот как я изменил код PHP в источнике PHPWiki

// update pagescore
//old way... 
/*     
mysql_query("DELETE FROM $WikiScoreStore", $dbi["dbc"]);
mysql_query("INSERT INTO $WikiScoreStore"
                 ." SELECT w1.topage, COUNT(*) FROM $WikiLinksStore AS w1, $WikiLinksStore AS w2"
                 ." WHERE w2.topage=w1.frompage GROUP BY w1.topage", $dbi["dbc"]);

*/

//delete this pagescore            
mysql_query("DELETE FROM $WikiScoreStore WHERE pagename='$frompage'", $dbi["dbc"]);
//insert just this pagescore
mysql_query("INSERT INTO $WikiScoreStore" 
                    ." SELECT w1.topage, COUNT(*) FROM $WikiLinksStore AS w1, $WikiLinksStore AS w2"
                ." WHERE w2.topage=w1.frompage AND w1.topage='$frompage' GROUP BY w1.topage", $dbi["dbc"]);

Поскольку этот код изменяется и индекс изменяется, у меня нет медленных запросов. Спасибо S.O.!

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