Индексирование MySQL VarChar

10

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

Вот структура:

CREATE TABLE IF NOT EXISTS `blogentries` (
  `id_id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `title_id` varchar(100) COLLATE latin1_german2_ci NOT NULL,
  `entry_id` varchar(5000) COLLATE latin1_german2_ci NOT NULL,
  `date_id` int(11) NOT NULL,
  PRIMARY KEY (`id_id`)
)
ENGINE=MyISAM
DEFAULT CHARSET=latin1
COLLATE=latin1_german2_ci
AUTO_INCREMENT=271;

Запрос, подобный следующему, правильно использует индекс:

EXPLAIN SELECT id_id,title_id FROM blogentries ORDER by id_id DESC
+ ---- + ------------- + ------------- + ------- + -------- ------- + --------- + --------- + ------ + ------ + -------- ----- +
| id | select_type | стол | тип | возможные_ключи | ключ | key_len | ref | строки | Extra |
+ ---- + ------------- + ------------- + ------- + -------- ------- + --------- + --------- + ------ + ------ + -------- ----- +
| 1 | ПРОСТО | Blogentries | индекс | NULL | ПЕРВИЧНЫЙ | 114 | NULL | 126 | Используя индекс |
+ ---- + ------------- + ------------- + ------- + -------- ------- + --------- + --------- + ------ + ------ + -------- ----- +

Тем не менее, когда я добавляю entry_idв SELECTзапрос он использует файловую сортировку

EXPLAIN SELECT id_id,title_id,entry_id FROM blogentries ORDER by id_id DESC
+ ---- + ------------- + ------------- + ------ + --------- ------ + ------ + --------- + ------ + ------ + ------------ ---- +
| id | select_type | стол | тип | возможные_ключи | ключ | key_len | ref | строки | Extra |
+ ---- + ------------- + ------------- + ------ + --------- ------ + ------ + --------- + ------ + ------ + ------------ ---- +
| 1 | ПРОСТО | Blogentries | ВСЕ | NULL | NULL | NULL | NULL | 126 | Использование сортировки файлов |
+ ---- + ------------- + ------------- + ------ + --------- ------ + ------ + --------- + ------ + ------ + ------------ ---- +

Мне было интересно, почему это происходит и как я могу избежать этого? Это из-за того VarChar, и это должно быть изменено на что-то другое?

Я пытаюсь , чтобы все мои запросы используют индекс , как я бег в максимум Handler_read_rndи Handler_read_rnd_nextценность.

Если вам нужна какая-либо другая информация, я тоже могу опубликовать ее.


источник
Сортировка файлов означает, что она выполняет сортировку на диске.
Кермит,
Попробуйте добавить WHERE 1=1ко второму запросу.
Кермит
Какая версия MySQL это? Какой у вас размер буфера сортировки ( SELECT @@sort_buffer_size)?
@njk filesort является результатом части запроса 'ORDER BY'
1
@TashPemhiwa Не обязательно, смотрите первое утверждение.
Кермит

Ответы:

6

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

Джо Стефанелли
источник
Конечно, MySQL должен использовать индекс для ORDER BY?
eggyal
@eggyal Нет, если он слишком большой для памяти.
Кермит,
@njk: Это не имеет смысла ... он может проходить по индексу по порядку, без необходимости загружать все это в память. Результаты будут отсортированы без необходимости выполнять сортировку файлов.
eggyal
@eggyal Я бы поставил под сомнение размер varchar(5000).
Кермит
@njk: Но этот столбец отсутствует в индексе и не используется в сортировке.
eggyal
2

Как описано в разделе « ORDER BYОптимизация» :

Для медленных запросов, для которых filesortне используется, попробуйте снизить max_length_for_sort_dataзначение, подходящее для запуска a filesort.

В своей статье в блоге « Что такое read_rnd_buffer_size» , Петр Зайцев объясняет:

Для меня это означает, что начиная с MySQL 4.1 эта опция используется в узком диапазоне случаев - если вы извлекаете несколько полей (меньше чем max_length_for_sort_data ), данные должны храниться в буфере сортировки и в файле сортировки, чтобы не было необходимости в read_rnd_buffer, если выбранные столбцы длинные, поэтому они длиннее max_length_for_sort_data, это часто означает, что среди них есть столбцы TEXT / BLOB. Однако он будет использоваться, если имеется большое количество столбцов или используются длинные столбцы VARCHAR - для создания строки, длина которой превышает max_length_for_sort_data в ее статическом представлении, требуется всего пара UTF8 VARCHAR (255) .

Это говорит о том, что max_length_for_sort_dataэто ограничение на общий размер столбцов, которые вы выбираете, выше которого filesortбудет использоваться сортировка вместо индекса.

В вашем случае выбор entry_id(5002 байта) принимает общий размер, превышающий значение по умолчанию, равное 1 КБ, и, следовательно filesort, используется. Чтобы поднять лимит до 8 КБ, вы можете сделать:

SET SESSION max_length_for_sort_data = 8192;
eggyal
источник
У меня есть таблица с настройкой, очень похожей на эту, и эта настройка не вызывает каких-либо изменений в использовании файловой сортировки.
@muffinista: это интересно. Я полагаю, это может быть связано с некоторыми другими настройками буфера, согласно ответу @ RolandoMySQLDBA ?
eggyal
2

Вы получили много интересных ответов здесь, но никто точно не ответил на вопрос - почему это происходит? Насколько я понимаю, когда запрос SELECT содержит данные переменной длины в MySQL и нет индекса, который соответствует ВСЕМ запрошенных столбцов, он всегда будет использовать файловую сортировку. Размер данных здесь не очень важен. Трудно найти прямой ответ на этот вопрос в документации по MySQL, но вот хороший пост в блоге, где кто-то испытывает проблему, очень похожую на вашу.

Смотрите также: 10 советов по оптимизации запросов MySQL (которые не отстой) .

Таким образом, если у него есть возможность иметь индекс для entry_id, вы можете добавить его и все готово. Но я сомневаюсь, что это вариант, так что делать?

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

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

Вот пара других ответов на SO, которые обсуждают этот вопрос:

Сообщество
источник
Первый запрос OP « содержит данные переменной длины в MySQL, и нет индекса, который соответствует ВСЕМ запрошенных столбцов », но, по- filesortвидимому, в этом случае не использовался. Я также думаю, что даже сортировка небольшой таблицы в одной памяти может оказаться неприемлемым ударом по производительности: например, если запрос выполняется много (и таблица изменяется так, что кэши не могут использоваться).
eggyal
У меня нет времени, чтобы проверить это, но мне интересно, если это вызвано наличием VARCHAR, который требует 2 байта для хранения длины, как указано в dev.mysql.com/doc/refman/5.1/en/char. html - поэтому первый запрос соответствует этому пределу, а второй - нет.
0

Попробуйте добавить WHEREпредложение в ваши запросы.

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

http://dev.mysql.com/doc/refman/5.0/en/order-by-optimization.html


источник
Но в этом случае ORDER BY делает соответствует индексу точно, поэтому нет необходимости иметь WHEREпункт.
eggyal
У меня есть предложение where в реальном запросе на сайте, так что я знаю, что это не причина сортировки файлов. Мне интересно, если это использование varchar?
0

Насколько мне известно, varchar может содержать не более 8000 байт, что составляет примерно 4000 символов. Таким образом, кажется, что 5000 превышает предел хранилища, и в этом случае, вероятно, причина, по которой сортировка испортилась.

"varchar [(n | max)] Данные переменной длины, не являющиеся символами Unicode. n может быть значением от 1 до 8 000. max указывает, что максимальный размер хранилища составляет 2 ^ 31-1 байт. Размер хранилища является фактическим длина введенных данных + 2 байта. Длина вводимых данных может быть 0 символов. Синонимы SQL-2003 для varchar различаются символами или символами. "

Надеюсь, что это ответ на ваш вопрос


источник
Как указана в соответствии с и типами : " Значение в столбцах VARCHAR представляют собой строка переменной длины Длина может быть указана в качестве значения от 0 до 255 до MySQL 5.0.3, и от 0 до 65535 в 5.0.3 и более поздних версиях эффективные.. Максимальная длина a в MySQL 5.0.3 и более поздних версиях зависит от максимального размера строки (65 535 байт, который является общим для всех столбцов) и используемого набора символов. "CHARVARCHARVARCHAR
eggyal
0

У вас есть только 126 строк в вашей таблице. Даже если максимальный размер каждой строки составляет около 5 КБ, это означает, что общий объем для чтения с диска составляет всего около 600 КБ - это не так уж много. Честно говоря, это очень небольшое количество, вероятно, меньше, чем размер кеша большинства современных дисков.

Теперь, если серверу нужно получить ваши данные для выполнения вашего запроса, самая дорогая операция - это прочитать их с диска. Но чтение в соответствии с порядком индекса НЕ всегда является самым быстрым способом сделать это, особенно когда объем данных настолько мал.

В вашем случае гораздо эффективнее считывать данные всей таблицы с диска как отдельный блок в память (возможно, всего за одну операцию чтения с диска или выполнять поиск), а затем сортировать их в ОЗУ для удовлетворения ORDER BY, который является мгновенным по сравнению с диском операция чтения. Если сервер считывает ваши данные в соответствии с индексом, ему придется выполнить до 126 (упс!) Операций чтения, многократно просматривая один и тот же файл данных.

Другими словами, последовательное сканирование не всегда плохо, и MySQL не обязательно глупо. Если вы попытаетесь заставить mysql использовать этот индекс, он, скорее всего, будет работать медленнее, чем последовательное сканирование, которое у вас сейчас есть.

И причина, по которой он использовал индекс, когда поле размером 5 КБ не было включено, заключается в том, что извлеченные данные не составляли 99% данных в таблице. Когда вы включили свое поле размером 5 КБ, теперь запрос должен прочитать 99% данных, и дешевле будет прочитать все это и впоследствии отсортировать его в памяти.

MVP
источник
Похоже, что вы запутываете некоторые вещи из « Как избежать полных сканирований таблицы» , которые связаны с использованием индекса при выполнении JOINусловий и WHEREпредложений, а не ORDER BYпредложений.
eggyal
Точно наоборот. В этом конкретном случае полное сканирование таблицы - это ХОРОШО, просто потому, что оно БЫСТРЕЕ, чем чтение по порядку индекса.
0

Какую версию MySQL вы используете?

В 5.1 я попытался настроить ваш сценарий и заполнил некоторые фиктивные данные. Используя предоставленные вами SQL-запросы, я получаю только сканирование таблицы каждый раз в соответствии с EXPLAIN. По умолчанию при использовании order by MYSQL используется сортировка файлов, даже если первичный индекс используется в порядке по.


источник