Коли я повинен використовувати складений індекс?


133
  1. Коли я повинен використовувати складений індекс у базі даних?
  2. Що таке розширення продуктивності за допомогою складеного індексу)?
  3. Чому я повинен використовувати складний індекс?

Наприклад, у мене є homesтаблиця:

CREATE TABLE IF NOT EXISTS `homes` (
  `home_id` int(10) unsigned NOT NULL auto_increment,
  `sqft` smallint(5) unsigned NOT NULL,
  `year_built` smallint(5) unsigned NOT NULL,
  `geolat` decimal(10,6) default NULL,
  `geolng` decimal(10,6) default NULL,
  PRIMARY KEY  (`home_id`),
  KEY `geolat` (`geolat`),
  KEY `geolng` (`geolng`),
) ENGINE=InnoDB  ;

Чи має сенс для мене використовувати складений індекс для обох geolatі geolngтаких, що:

Я замінюю:

  KEY `geolat` (`geolat`),
  KEY `geolng` (`geolng`),

з:

KEY `geolat_geolng` (`geolat`, `geolng`)

Якщо так:

  • Чому?
  • Що таке розширення продуктивності за допомогою складеного індексу)?

ОНОВЛЕННЯ:

Оскільки багато людей заявили, що це повністю залежить від запитів, які я виконую, нижче наведено найбільш поширені запити:

SELECT * FROM homes
WHERE geolat BETWEEN ??? AND ???
AND geolng BETWEEN ??? AND ???

ОНОВЛЕННЯ 2:

За допомогою наступної схеми бази даних:

CREATE TABLE IF NOT EXISTS `homes` (
  `home_id` int(10) unsigned NOT NULL auto_increment,
  `primary_photo_group_id` int(10) unsigned NOT NULL default '0',
  `customer_id` bigint(20) unsigned NOT NULL,
  `account_type_id` int(11) NOT NULL,
  `address` varchar(128) collate utf8_unicode_ci NOT NULL,
  `city` varchar(64) collate utf8_unicode_ci NOT NULL,
  `state` varchar(2) collate utf8_unicode_ci NOT NULL,
  `zip` mediumint(8) unsigned NOT NULL,
  `price` mediumint(8) unsigned NOT NULL,
  `sqft` smallint(5) unsigned NOT NULL,
  `year_built` smallint(5) unsigned NOT NULL,
  `num_of_beds` tinyint(3) unsigned NOT NULL,
  `num_of_baths` decimal(3,1) unsigned NOT NULL,
  `num_of_floors` tinyint(3) unsigned NOT NULL,
  `description` text collate utf8_unicode_ci,
  `geolat` decimal(10,6) default NULL,
  `geolng` decimal(10,6) default NULL,
  `display_status` tinyint(1) NOT NULL,
  `date_listed` timestamp NOT NULL default CURRENT_TIMESTAMP,
  `contact_email` varchar(100) collate utf8_unicode_ci NOT NULL,
  `contact_phone_number` varchar(15) collate utf8_unicode_ci NOT NULL,
  PRIMARY KEY  (`home_id`),
  KEY `customer_id` (`customer_id`),
  KEY `city` (`city`),
  KEY `num_of_beds` (`num_of_beds`),
  KEY `num_of_baths` (`num_of_baths`),
  KEY `geolat` (`geolat`),
  KEY `geolng` (`geolng`),
  KEY `account_type_id` (`account_type_id`),
  KEY `display_status` (`display_status`),
  KEY `sqft` (`sqft`),
  KEY `price` (`price`),
  KEY `primary_photo_group_id` (`primary_photo_group_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=8 ;

Використання наступного SQL:

EXPLAIN SELECT  homes.home_id,
                    address,
                    city,
                    state,
                    zip,
                    price,
                    sqft,
                    year_built,
                    account_type_id,
                    num_of_beds,
                    num_of_baths,
                    geolat,
                    geolng,
                    photo_id,
                    photo_url_dir
            FROM homes
            LEFT OUTER JOIN home_photos ON homes.home_id = home_photos.home_id
                AND homes.primary_photo_group_id = home_photos.home_photo_group_id
                AND home_photos.home_photo_type_id = 2
            WHERE homes.display_status = true
            AND homes.geolat BETWEEN -100 AND 100
            AND homes.geolng BETWEEN -100 AND 100

EXPLAIN повертає:

id  select_type  table        type  possible_keys                                    key                  key_len  ref     rows  Extra
----------------------------------------------------------------------------------------------------------
1   SIMPLE       homes        ref   geolat,geolng,display_status                     display_status       1        const   2     Using where
1  SIMPLE        home_photos  ref   home_id,home_photo_type_id,home_photo_group_id   home_photo_group_id  4        homes.primary_photo_group_id   4  

Я не зовсім розумію, як читати команду EXPLAIN. Це добре чи погано виглядає Наразі я НЕ використовую складений індекс для геолату та geolng. Чи повинен я бути?

Відповіді:


111

Ви повинні використовувати складений індекс, коли ви використовуєте запити, які користуються ним. Складений індекс, який виглядає приблизно так:

index( column_A, column_B, column_C )

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

index( column_A, column_B, column_C )
index( column_A, column_B )
index( column_A )

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

index( column_A, column_C )

Зверніть увагу, як колонка_B відсутня.

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


1
Познач, я оновив свій початковий пост (оновлення 2). Це мій фактичний запит. Моя фактична схема DB. І те, що повертає команда EXPLAIN. Отже, з цією інформацією - чи повинен я використовувати складений індекс. Мені все одно незрозуміло. Заздалегідь спасибі.
Тедді

Позначте, чи відповідає складений індекс у вашій відповіді індексу (стовпець_C)?
Теохаров Борис Д.

Я не впевнений, що розумію ваше запитання. Але якщо ви запитуєте, чи допоможе індекс (A, B, C) запит, який фільтрує у стовпці С, відповідь, як правило, буде ні, він не використовував би індекс для фільтрації. Однак він може використовувати індекс для усунення сканування таблиці, якщо ви вибираєте лише підмножину ABC. Отже, це інакше, але пов’язано. Але для типового використання індексів для фільтрації відповідь - ні.
Марк Канлас

1
-1 тому, що складений індекс не допомагає WHERE geolat BETWEEN ??? AND ??? AND geolng BETWEEN ??? AND ???. Він зупиниться після першого поля. Відповідь із "Переповнення питань" пояснює, чому.
Рік Джеймс

1
@felwithe MySQL може використовувати лише один індекс для кожної з таблиць у запиті (винятки є, наприклад, об'єднання індексів). Що в ідеалі означає, що таблиця в запиті повинна використовувати єдиний індекс для всіх, де-пункт, приєднання таблиці, групування та упорядкування. Тож окремий індекс у кожному стовпчику може працювати не завжди, але складений індекс може зробити магію.
AKHIL MATHEW

56

Уявіть, що у вас є такі три запити:

Запит I:

SELECT * FROM homes WHERE `geolat`=42.9 AND `geolng`=36.4

Запит II:

SELECT * FROM homes WHERE `geolat`=42.9

Запит III:

SELECT * FROM homes WHERE `geolng`=36.4

Якщо у вас є окремий індекс на стовпець, усі три запити використовують індекси. У MySQL, якщо у вас є складений індекс ( geolat, geolng), лише запити I і запит II (для яких використовується перша частина індексу композиту) використовують індекси. У цьому випадку для запиту III потрібен повний пошук таблиці.

На кількох стовпчикових індексах розділі " з кількома стовпцями" в посібнику чітко пояснено, як працюють кілька індексів стовпців, тому я не хочу повторно вводити посібник.

На сторінці довідника MySQL :

Індекс з декількома стовпцями можна вважати відсортованим масивом, що містить значення, які створюються шляхом об'єднання значень індексованих стовпців .

Якщо ви використовуєте відокремлений індекс для стовпців геолату та geolng, у вашій таблиці є два різних індексу, які можна шукати незалежно.

INDEX geolat
-----------
VALUE RRN
36.4  1
36.4  8
36.6  2
37.8  3
37.8  12
41.4  4

INDEX geolng
-----------
VALUE RRN
26.1  1
26.1  8
29.6  2
29.6  3
30.1  12
34.7  4

Якщо ви використовуєте складений індекс, у вас є лише один індекс для обох стовпців:

INDEX (geolat, geolng)
-----------
VALUE      RRN
36.4,26.1  1
36.4,26.1  8
36.6,29.6  2
37.8,29.6  3
37.8,30.1  12
41.4,34.7  4

RRN - відносна кількість запису (для спрощення можна сказати ID). Перші два індекси генеруються окремо, а третій індекс - складеним. Як ви бачите, ви можете шукати на основі geolng на складеному, оскільки він індексується геолатом, проте можна шукати по geolat або "geolat AND geolng" (оскільки geolng - індекс другого рівня).

Також ознайомтеся з розділом, як MySQL використовує індекси вручну.


1
Насправді у мене немає жодного запиту. Мій запит вказаний у оригінальній публікації. Мій запит - повернути будинки в межах квадратної сітки. Я знаю про просторові і не намагаюся обчислювати відстані. Мені просто хочеться знати, чи має сенс використовувати складений індекс, коли я намагаюся відобразити всі будинки в певній геомережі (наприклад, мікрорайон / місто / округ)
Тедді

Eyazici, я оновив свій початковий пост (оновлення 2). Це мій фактичний запит. Моя фактична схема DB. І те, що повертає команда EXPLAIN. Отже, з цією інформацією - чи повинен я використовувати складений індекс. Мені все одно незрозуміло. Заздалегідь дякую
Тедді

@ "Насправді у мене немає жодного із цих запитів." Насправді у вас є, я використовував просту умову ГДЕ, щоб пояснити базову логіку. Під час використання умовного (тобто ДЕРЖАВИ) стовпця MySQL намагається використовувати індекси, коли це можливо. "x МІЖ a і b" схоже на "x> a AND x <b". У запиті ви умовно використовували колонки geolng та geolat. Якщо ви використовуєте компонентний індекс "(geolat, geolng)" ваш "І geolng МІЖ ??? І ???" умовно не отримує переваг індексу (це для MySQL). Тому слід використовувати окремий індекс на стовпець для свого сценарію.
Емре Язичі

Я не розумію. Чому я повинен використовувати окремі індекси для geolat та geolng, коли Я ВЖЕ завжди виконую запит, який включає обидва стовпці
Тедді

1
Ні. Коли зустрічається "діапазон" (як і у випадку BETWEEN), подальші поля індексу не враховуються! Тож складний показник не кращий.
Рік Джеймс

19

Можливо, помилкове уявлення про те, що робить складений індекс. Багато людей думають, що складений індекс може бути використаний для оптимізації пошукового запиту до тих пір, поки whereпропозиція охоплює індексовані стовпці, у вашому випадку geolatта geolng. Давайте поглибимося глибше:

Я вважаю, що ваші дані про координати будинків будуть випадковими десятичними знаками:

home_id  geolat  geolng
   1    20.1243  50.4521
   2    22.6456  51.1564
   3    13.5464  45.4562
   4    55.5642 166.5756
   5    24.2624  27.4564
   6    62.1564  24.2542
...

Оскільки geolatі geolngзначення навряд чи повторюються. Складений індекс на geolatі geolngвиглядатиме приблизно так:

index_id  geolat  geolng
   1     20.1243  50.4521
   2     20.1244  61.1564
   3     20.1251  55.4562
   4     20.1293  66.5756
   5     20.1302  57.4564
   6     20.1311  54.2542
...

Тому другий стовпчик складеного індексу в принципі марний ! Швидкість вашого запиту із складеним індексом, ймовірно, буде схожа на індекс лише у geolatстовпці.

Як згадував Вілл, MySQL забезпечує підтримку просторового розширення . Просторова точка зберігається в одному стовпчику замість двох окремих lat lngстовпців. Просторовий індекс може бути застосований до такої колонки. Однак ефективність може бути завищена на основі мого особистого досвіду. Можливо, просторовий індекс не вирішує двовимірну задачу, а лише прискорює пошук за допомогою R-Trees з квадратичним розщепленням .

Виправданим є те, що просторова точка споживає набагато більше пам’яті, оскільки вона використовувала восьмибайтові подвійні точні числа для зберігання координат. Виправте мене, якщо я помиляюся.


5

Складові індекси є дуже потужними, оскільки вони:

  • Забезпечення цілісності структури
  • Увімкнути сортування за відфільтрованим ідентифікатором

ЦІЛЬНІСТЬ СТРУКТУРНОЇ СТРУКТУРИ

Складені індекси - це не просто інший тип індексу; вони можуть надати структуру НЕОБХІДНОСТІ в таблицю, застосовуючи цілісність як Первинний ключ.

Innodb Mysql підтримує кластеризацію, і наступний приклад ілюструє, чому може бути необхідний складений індекс.

Для створення друзів таблиці (тобто для соціальної мережі) необхідно 2 колонки: user_id, friend_id.

Структура столу

user_id (medium_int)
friend_id (medium_int)

Primary Key -> (user_id, friend_id)

В силу первинного ключа (ПК) є унікальним і, створивши складений ПК, Innodb автоматично перевірить, чи не user_id, friend_idіснує дублікатів при додаванні нової записи. Це очікувана поведінка, оскільки жоден користувач, наприклад, не повинен мати більше 1 запису (зв’язок зв’язку) friend_id = 2.

Без складеного ПК ми можемо створити цю схему за допомогою сурогатного ключа:

user_friend_id
user_id
friend_id

Primary Key -> (user_friend_id)

Тепер, коли буде доданий новий запис, нам доведеться перевірити, user_id, friend_idчи не існує попереднього запису з комбінацією .

Таким чином, складений індекс може забезпечити цілісність структури.

МОЖЛИВО СОРТИРУВАТИ НА ФІЛЬТРОВАНИЙ ІД

Дуже часто впорядковується набір записів за часом публікації (часова марка або дата). Зазвичай це означає публікацію на заданому ідентифікаторі. Ось приклад

Таблиця User_Wall_Posts (подумайте, чи стіни Facebook на стінах)

user_id (medium_int)
timestamp (timestamp)
author_id (medium_int)
comment_post (text)

Primary Key -> (user_id, timestamp, author_id)

Ми хочемо здійснити запит і знайти всі повідомлення для user_id = 10та сортувати повідомлення з коментарями за timestamp(датою).

SQL QUERY

SELECT * FROM User_Wall_Posts WHERE user_id = 10 ORDER BY timestamp DES

Композитний ПК дозволяє Mysql фільтрувати та сортувати результати за допомогою індексу; Mysql не доведеться використовувати тимчасовий файл або fileort для отримання результатів. Без складеного ключа це не було б можливим і викликало б дуже неефективний запит.

Таким чином, складові ключі дуже потужні і підходять більше, ніж проста проблема "Я хочу шукати, column_a, column_bтому я буду використовувати складені ключі. Для моєї поточної схеми бази даних у мене стільки ж складових ключів, скільки одиночних клавіш. Не забувайте використання складеного ключа!


5

Композитні індекси корисні для

  • 0 або більше "=" пунктів плюс
  • максимум один пункт про діапазон.

Складений індекс не може обробляти два діапазони. Я обговорюю це далі у своїй індексній кулінарній книзі .

Знайти найближчий - якщо питання дійсно стосується оптимізації

WHERE geolat BETWEEN ??? AND ???
  AND geolng BETWEEN ??? AND ???

то жоден індекс не може реально обробити обидва виміри.

Натомість треба "думати з коробки". Якщо один вимір реалізований за допомогою розділення, а інший реалізований шляхом ретельного вибору PRIMARY KEY, можна отримати значно кращу ефективність для дуже великих таблиць пошуку lat / lng. Мій блог latlng розглядає деталі, як реалізувати "знайти найближчого" на земній кулі. Він включає код.

Це PARTITIONsсмуги діапазонів широти. PRIMARY KEYНавмисно починається з довготою , так що корисні рядки, ймовірно, будуть в тому ж блоці. Зберігається звичайний оркеструє безладний код для виконання order by... limit...та вирощування «квадрата» навколо цілі, поки у вас не буде достатньо кав’ярень (або чого завгодно). Він також піклується про обчислення великого кола та обробку дателіни та полюсів.

Більше

Я написав ще один блог; він порівнює 5 способів пошуку lat / lng пошуку: http://mysql.rjweb.org/doc.php/latlng#representation_choices (Посилання, наведене вище, згадується як один із 5.) Один із інших способів - це, і це вказує, що вони оптимальні для конкретного випадку :

INDEX(geolat, geolng),
INDEX(geolng, geolat)

Тобто важливим є наявність обох стовпців у двох індексах та відсутність одноколонних індексів на геолаті та geolng.


1

Чорно-білого немає, один розмір відповідає всім відповідям.

Ви повинні використовувати складений індекс, коли ваше запит робоче навантаження виграє від одного.

Для того, щоб визначити це, потрібно профілювати навантаження на запит.

Складений індекс починає грати, коли запити можуть бути задоволені повністю з цього індексу.

ОНОВЛЕННЯ (у відповідь на редагування на розміщене запитання): Якщо ви вибираєте * з таблиці, складений індекс може використовуватися, він може не робити. Вам потрібно буде запустити ПОЯСНИЙ ПЛАН, щоб бути впевненим.


Чи має сенс використовувати складений індекс для географічних даних про місцезнаходження (широта та довгота)?
Тедді

1
Це повністю залежить від того, які запити проводяться проти цієї таблиці.
Мітч Пшеничний

Я оновив свою оригінальну публікацію, щоб включити найпоширеніший запит, що виконується. Дивись вище.
Тедді

1

Для просторового пошуку потрібен алгоритм R-Tree , який дозволяє дуже швидко шукати географічні райони. Саме те, що потрібно для цієї роботи.

У деяких базах даних вбудовані просторові індекси. Швидкий пошук в Google показує, що в MySQL 5 є (що, дивлячись на ваш SQL, я думаю, ви використовуєте MySQL).


1

Складений індекс може бути корисним, коли ви хочете оптимізувати group byпункт (перегляньте цю статтю http://dev.mysql.com/doc/refman/5.0/en/group-by-optimization.html ). Зверніть увагу:

Найважливіші передумови використання індексів для GROUP BY - це те, що всі стовпці GROUP BY посилають атрибути з одного індексу і що індекс зберігає свої ключі в порядку (наприклад, це індекс BTREE, а не індекс HASH)


GROUP BYне згадувалося.
Рік Джеймс

Де не було зазначено? :) Про це, очевидно, йдеться у статті, про яку я згадував. І він відповідає на питання, які задавали: Коли я повинен використовувати складений індекс у базі даних? Що таке розширення продуктивності за допомогою складеного індексу)? Чому я повинен використовувати складний індекс?
Олександр

Виправлення: GROUP BYОП не згадувалося.
Рік Джеймс

Звичайно, це була відповідь - один із випадків, коли ми використовували складений індекс у базі даних.
Олександр

0

Я з @Mitch, повністю залежить від ваших запитів. На щастя, ви можете в будь-який час створити та скинути індекси, і ви можете додати ключове слово EXPLAIN до ваших запитів, щоб побачити, чи використовується аналізатор запитів.

Якщо ви шукаєте точну ширину / довгу пару, цей індекс, швидше за все, має сенс. Але ви, мабуть, будете шукати будинки на певній відстані від певного місця, тож ваші запити будуть виглядати приблизно так (див. Джерело ):

select *, sqrt(  pow(h2.geolat - h1.geolat,  2) 
               + pow(h2.geolng - h1.geolng, 2) ) as distance
from homes h1, homes h2
where h1.home_id = 12345 and h2.home_id != h1.home_id
order by distance

і, швидше за все, індекс не буде корисним. Для геопросторових запитів, вам потрібно що - щось на зразок цього .

Оновлення: за допомогою цього запиту:

SELECT * FROM homes
WHERE geolat BETWEEN ??? AND ???
AND geolng BETWEEN ??? AND ???

Аналізатор запитів може використовувати індекс лише для геолата або індекс лише для geolng, або, можливо, обидва індекси. Я не думаю, що це використовувало б складений індекс. Але легко спробувати кожну з цих перестановок на реальному наборі даних, а потім (а) подивитися, що вам повідомляє EXPLAIN, і (b) виміряти час, який запит дійсно потребує.


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