MySQL MyISAM производительность таблицы ... мучительно, мучительно медленно - PullRequest
3 голосов
/ 09 мая 2009

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

pagegroup
* pagegroupid
* name

имеет 3600 строк

page
* pageid
* pagegroupid
* data

ссылки на страницу группы; имеет 10000 рядов; может содержать от 1 до 700 строк на группу страниц; столбец данных имеет тип mediumtext, и в нем содержится 100–200 кбайт данных на строку

userdata
* userdataid
* pageid
* column1
* column2
* column9

страница ссылок; имеет около 300 000 строк; может иметь около 1-50 строк на странице

Приведенная выше структура довольно проста, проблема в том, что соединение пользовательских данных с группой страниц происходит очень медленно, даже если я проиндексировал все столбцы, которые должны быть проиндексированы. Время, необходимое для выполнения запроса для такого объединения (userdata inner_join page inner_join pagegroup) превышает 3 минуты. Это очень медленно, учитывая тот факт, что я вообще не выбираю столбец данных. Пример запроса, который занимает слишком много времени:

SELECT userdata.column1, pagegroup.name
FROM userdata
INNER JOIN page USING( pageid )
INNER JOIN pagegroup USING( pagegroupid )

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

Редактировать # 1

Объясните, возвращает следующий бред:

id  select_type  table      type    possible_keys        key      key_len  ref                         rows    Extra
1   SIMPLE       userdata   ALL     pageid                                                             372420
1   SIMPLE       page       eq_ref  PRIMARY,pagegroupid  PRIMARY  4        topsecret.userdata.pageid   1
1   SIMPLE       pagegroup  eq_ref  PRIMARY              PRIMARY  4        topsecret.page.pagegroupid  1

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

SELECT
u.field2, p.pageid
FROM
userdata u
INNER JOIN page p ON u.pageid = p.pageid;
/*
0.07 sec execution, 6.05 sec fecth
*/

id  select_type  table  type    possible_keys  key      key_len  ref                rows     Extra
1   SIMPLE       u      ALL     pageid                                              372420
1   SIMPLE       p      eq_ref  PRIMARY        PRIMARY  4        topsecret.u.pageid 1        Using index

SELECT
p.pageid, g.pagegroupid
FROM
page p
INNER JOIN pagegroup g ON p.pagegroupid = g.pagegroupid;
/*
9.37 sec execution, 60.0 sec fetch
*/

id  select_type  table  type   possible_keys  key          key_len  ref                      rows  Extra
1   SIMPLE       g      index  PRIMARY        PRIMARY      4                                 3646  Using index
1   SIMPLE       p      ref    pagegroupid    pagegroupid  5        topsecret.g.pagegroupid  3     Using where

Мораль истории

Храните средние / длинные текстовые столбцы в отдельной таблице, если вы столкнулись с проблемами производительности, такими как эта.

Ответы [ 6 ]

4 голосов
/ 09 мая 2009

Каков тип данных и назначение columnX в таблице пользовательских данных? Следует отметить, что любой текстовый тип данных (т. Е. Исключая char, varchar) заставляет создавать любые временные таблицы на диске. Теперь, когда вы выполняете прямое объединение без условий, группировки или упорядочения, вероятно, ему не понадобятся временные таблицы, за исключением агрегирования окончательного результата.

Думаю, было бы очень полезно, если бы вы показали нам, как создаются ваши индексы. Следует помнить одну вещь: в то время как InnoDB объединяет первичный ключ таблицы с каждым индексом, MyISAM этого не делает. Это означает, что если вы индексируете столбец name и ищете его с помощью LIKE, но все же хотите получить id группы страниц; Тогда запрос все равно должен был бы посетить таблицу, чтобы получить id вместо того, чтобы иметь возможность извлечь его из индекса.

Что означает, в вашем случае, если я правильно понимаю ваш комментарий к apphacker , это получить имя каждой группы страниц пользователей. Оптимизатор запросов хотел бы использовать индекс для объединения, но для каждого результата он также должен был бы посетить таблицу, чтобы получить имя группы страниц. Если ваш тип данных для name не больше, чем умеренный varchar, то есть без текста, вы также можете создать индекс (id, name), который позволит запросу извлекать имя непосредственно из индекса.

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

  1. Этот столбец исключен из вашего запроса, я полагаю?
  2. Вы также можете попытаться отделить данные страницы от страницы «конфигурации», то есть к какой группе она принадлежит. Тогда у вас, вероятно, будет что-то вроде:
    • Страницы
      • PageId
      • pageGroupId
    • PageData
      • PageId
      • данные

Мы надеемся, что это позволит вам быстрее присоединиться, поскольку ни один столбец в Pages не занимает много места. Затем, когда вам необходимо отобразить определенную страницу, вы присоединяетесь к таблице PageData в столбце pageId, чтобы получить данные, необходимые для отображения конкретной страницы.

2 голосов
/ 09 мая 2009

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

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

Индекс может выглядеть так:

column1, pageid

Это должно быть не кластеризовано, иначе это будет часть большого стола, нанося поражение его цели. См. эту страницу , чтобы узнать, как MySQL решает, какой индекс кластеризовать. Кажется, что самый простой способ - убедиться, что у вас есть первичный ключ на pageid, который будет кластеризован, поэтому вторичный индекс column1 + pageid будет некластеризованным.

2 голосов
/ 09 мая 2009

Простой способ выяснить, что MySQL делает с вашим запросом, - это объяснить вам его. Запустите это и посмотрите на вывод:

EXPLAIN SELECT userdata.column1, pagegroup.name
FROM userdata
INNER JOIN page USING( pageid )
INNER JOIN pagegroup USING( pagegroupid )

MySQL сообщит вам, в каком порядке он обрабатывает запросы и какие индексы он использует. Тот факт, что вы создали индексы, не означает, что MySQL фактически использует их.

См. Также Оптимизация запросов с помощью EXPLAIN

EDIT

Вывод вашего EXPLAIN выглядит нормально. Он выполняет полную проверку таблицы пользовательских данных, но это нормально, так как вы хотите вернуть все строки в ней. Лучший способ оптимизировать это - переосмыслить ваше приложение. Вы действительно должны вернуть все строки 372K?

1 голос
/ 09 мая 2009

Похоже, вы выполняете объединение всех строк на userdata, а затем пытаетесь выбрать все. Это каждый page в pagegroup с userdata. Где пункт WHERE? Там нет LIMIT, сколько результатов вы хотели? Почему бы вам не подсчитать число строк в строке userdata в результате explain, это должно ускорить запрос. Хех.

1 голос
/ 09 мая 2009

Я бы начал с разбивки запроса, чтобы выяснить, есть ли одна медленная и одна быстрая части, или если обе медленные (извините, я не фанат синтаксиса USING, поэтому я собираюсь использовать ON):

SELECT 
  u.userdata, p.pageid
FROM
  userdata u
  INNER JOIN page p ON u.pageid = p.pageid

SELECT 
  p.pageid, g.pagegroupid
FROM
  page 
  INNER JOIN pagegroup g ON p.pagegroupid = g.pagegroupid

Что это тебе дает? Запуск с EXPLAIN EXTENDED даст дополнительные подсказки.

1 голос
/ 09 мая 2009

Одной из возможных проблем является то, что MySQL использует только один индекс на запрос, и, возможно, у вас нет ни одного индекса с этими столбцами - или оптимизатор запросов MySQL не выбирает его. Что EXPLAIN SELECT & c говорит вам здесь?

...