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 | посилання | ряди | Додатковий |
+ ---- + ------------- + ------------- + ------- + -------- ------- + --------- + --------- + ------ + ------ + -------- ----- +
| 1 | ПРОСТО | блогери | індекс | NULL | ПЕРШИЙ | 114 | NULL | 126 | Використання індексу |
+ ---- + ------------- + ------------- + ------- + -------- ------- + --------- + --------- + ------ + ------ + -------- ----- +

Тим НЕ менше, коли я додаю entry_idв SELECTзапиті він використовує FileSort

EXPLAIN SELECT id_id,title_id,entry_id FROM blogentries ORDER by id_id DESC
+ ---- + ------------- + ------------- + ------ + --------- ------ + ------ + --------- + ------ + ------ + ------------ ---- +
| id | select_type | стіл | тип | можливі_ключі | ключ | key_len | посилання | ряди | Додатковий |
+ ---- + ------------- + ------------- + ------ + --------- ------ + ------ + --------- + ------ + ------ + ------------ ---- +
| 1 | ПРОСТО | блогери | ВСІ | NULL | NULL | NULL | NULL | 126 | Використання файлів |
+ ---- + ------------- + ------------- + ------ + --------- ------ + ------ + --------- + ------ + ------ + ------------ ---- +

Мені було цікаво, чому це відбувається і як я можу цього уникнути? Це пов'язано з тим VarChar, що потрібно змінити на щось інше?

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

Якщо вам потрібна будь-яка інша інформація, я також можу розмістити її.


fileort означає, що він виконує сортування на диску.
Керміт

Спробуйте додати WHERE 1=1до свого другого запиту.
Керміт

Яка версія MySQL це? Який розмір буфера сортування ( SELECT @@sort_buffer_size)?

@njk filesort - результат запиту "ЗАМОВИТИ ПО"

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.

У своїй статті в блозі Що саме читається_більше_помірник , Петро Зайцев пояснює:

Для мене це означає, оскільки 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буде використано a замість сортування на основі індексу.

У вашому випадку вибір entry_id(5002 байтів) приймає загальний розмір над значенням за замовчуванням 1KiB цієї змінної і тому filesortвикористовується. Щоб підняти ліміт до 8KiB, ви можете:

SET SESSION max_length_for_sort_data = 8192;

У мене є таблиця з дуже подібною установкою до цієї, і ця настройка, здається, не викликає жодних змін у використанні файлового ряду.

@muffinista: Це цікаво. Я вважаю, що це може бути пов'язано з деякими іншими налаштуваннями буфера, відповідно до відповіді @ RolandoMySQLDBA ?
eggyal

2

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

Дивіться також: 10 порад щодо оптимізації запитів MySQL (які не всмоктуються) .

Отже, якщо його життєздатність мати індекс на entry_id, то ви можете додати його та бути готовим. Але я сумніваюся, що це варіант, і що робити?

Чи варто щось робити з цього приводу - це окреме питання. Важливо знати, що "fileort" в MySQL погано названий - це справді лише назва алгоритму, який використовується для сортування цього конкретного запиту, і в багатьох випадках сортування насправді відбудеться в пам'яті. Якщо ви не очікуєте, що ця таблиця значно зросте, це, мабуть, не велика справа.

З іншого боку, якщо ця таблиця матиме в ній мільйон рядків, у вас може виникнути проблеми. Якщо вам потрібно підтримати пагінацію запитів у цій таблиці, то тут може виникнути справді серйозна проблема. У такому випадку розділення даних змінної довжини у новій таблиці та проведення СПОЛУЧЕННЯ для її отримання - це достовірна оптимізація.

Ось ще кілька відповідей на ТА, які говорять навколо цього питання:


Перший запит ОП " містить дані змінної довжини в 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/uk/order-by-optimization.html


Але в цьому випадку ORDER BY робить відповідає індексу точно, тому немає необхідності мати WHEREпункт.
eggyal

У фактичного запиту на сайті є пункт «де», тому я знаю, що це не причина сортування файлів. Мені цікаво, чи це використання варчара?

0

Наскільки мені відомо, varchar може вмістити максимум 8000 байт, що становить приблизно 4000 символів. Таким чином, 5000, здавалося б, перевищують межу зберігання, і в цьому випадку, мабуть, причина, чому сортування заплутається.

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

Сподіваюся, що це відповість на ваше запитання


Як задокументовано у розділі " The CHARand VARCHARTypes" : " Значення у стовпцях VARCHAR - це рядки змінної довжини. Довжина може бути вказана як значення від 0 до 255 перед MySQL 5.0.3, і від 0 до 65,535 в версіях 5.0.3 та новіших версіях. Ефективна Максимальна довжина VARCHARв MySQL 5.0.3 та пізніших версій залежить від максимального розміру рядка (65,535 байт, який поділяється між усіма стовпцями) та використовуваного набору символів ".
eggyal

0

У вас всього 126 рядків у таблиці. Навіть якщо кожен рядок має розмір максимум близько 5 КБ, це означатиме, що загальний розмір для читання з диска становить лише близько 600 КБ - це не вся партія. Якщо чесно кажучи, це дуже мала кількість, ймовірно, менша за розмір кешу більшості сучасних дисководів.

Тепер, якщо сервер потребує отримання ваших даних, щоб виконати ваш запит, найдорожчою операцією є його зчитування з диска. Але, читаючи його відповідно до порядку індексів, НЕ завжди найшвидший спосіб зробити це, особливо коли кількість даних настільки мала.

У вашому випадку набагато ефективніше читати дані цілої таблиці з диска як єдиний блок в пам'ять (можливо, лише за одну операцію читання диска або шукати), а потім сортувати їх в оперативній пам'яті, щоб задовольнити ЗАМОВЛЕННЯ, що миттєво порівняно з диском операція зчитування. Якщо сервер читає ваші дані згідно з індексом, йому доведеться виконувати до 126 (ой!

Іншими словами, послідовне сканування НЕ завжди є поганою справою, і mysql не обов'язково є дурним. Якщо ви спробуєте змусити mysql використовувати цей індекс, він, швидше за все, буде працювати повільніше, ніж послідовне сканування, яке ви зараз маєте.

І причиною, чому БУЛО використовувати індекс, коли поле 5 КБ не було включено, є те, що тоді отримані дані не становили 99% даних у таблиці. Коли ви включили своє поле в 5 КБ, тепер запит повинен прочитати 99% даних, і дешевше прочитати всю річ і згодом відсортувати її в пам'яті.


Здається, що ви заплутаєте низку речей із " Як уникнути сканування повних таблиць" , які стосуються використання індексу в задовольняючих JOINумовах та WHEREпунктах, а не в ORDER BYзастереженнях.
eggyal

Точно навпаки. У цьому конкретному випадку повне сканування таблиці - ДОБРО, тому що це Швидше, ніж читання за порядком індексів.

0

Яку версію MySQL ви використовуєте?

В 5.1 я спробував налаштувати ваш сценарій і заповнив деякі фіктивні дані. Використовуючи надані вами SQL, я щоразу отримую сканування таблиці відповідно до ПОЯСНЕННЯ. За замовчуванням, коли ви використовуєте замовлення по MYSQL, вдається до файлуортиту, навіть якщо основний індекс використовується у порядку від.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.