Когда я должен использовать составной индекс? - PullRequest
119 голосов
/ 01 декабря 2009
  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`)

Если так:

  • Почему?
  • Что такое увеличение производительности при использовании составного индекса)?

UPDATE:

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

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. Это выглядит хорошо или плохо. Прямо сейчас я НЕ использую составной индекс для geolat и geolng. Должен ли я быть?

Ответы [ 9 ]

95 голосов
/ 01 декабря 2009

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

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 )

Обратите внимание, как отсутствует column_B.

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

48 голосов
/ 01 декабря 2009

Представьте, что у вас есть три следующих запроса:

Запрос 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 требует полного поиска по таблице.

Вкл. Индексы с несколькими столбцами * В разделе руководства 1017 * четко объясняется, как работают индексы с несколькими столбцами, поэтому я не хочу перепечатывать руководство вручную.

Со страницы Справочного руководства MySQL :

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

Если вы используете отдельный индекс для столбцов geolat и 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 или «geolat AND geolng» (поскольку geolng является индексом второго уровня).

Также взгляните на Как MySQL использует индексы раздел руководства.

18 голосов
/ 24 декабря 2011

Может быть неправильное представление о том, что делает составной индекс. Многие люди думают, что составной индекс можно использовать для оптимизации поискового запроса, если предложение 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.

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

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

6 голосов
/ 28 ноября 2015

Составные индексы полезны для

  • 0 или более "=", плюс
  • не более одного предложения диапазона.

Составной индекс не может обрабатывать два диапазона. Я обсуждаю это далее в моей книге указателей .

Найти ближайшего - Если вопрос действительно об оптимизации

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

затем no index может действительно обрабатывать оба измерения.

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

PARTITIONs - полосы широтных диапазонов. PRIMARY KEY преднамеренно начинается с долготы, так что полезные строки, вероятно, находятся в одном и том же блоке. Хранимая процедура организует грязный код для выполнения order by... limit... и для увеличения «квадрата» вокруг цели, пока у вас не будет достаточно кафе (или чего-то еще). Он также заботится о вычислениях большого круга и обработке линии даты и полюсов.

5 голосов
/ 29 декабря 2012

Составные индексы очень мощные, так как они:

  • Обеспечить целостность структуры
  • Включить сортировку по ФИЛЬТР. Идентификатору

ЕДИНСТВЕННАЯ СТРУКТУРА ЦЕЛОСТНОСТИ

Составные индексы - это не просто другой тип индекса; они могут предоставить НЕОБХОДИМУЮ структуру для таблицы путем обеспечения целостности в качестве первичного ключа.

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

Для создания таблиц друзей (то есть для социальной сети) нам нужны 2 столбца: user_id, friend_id.

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

user_id (medium_int)
friend_id (medium_int)

Primary Key -> (user_id, friend_id)

В силу того, что первичный ключ (PK) является уникальным, и при создании составного PK Innodb автоматически проверяет, что при добавлении новой записи дубликатов на user_id, friend_id не существует. Это ожидаемое поведение, так как ни у одного пользователя не должно быть более 1 записи (связь), например, friend_id = 2.

Без составного PK мы можем создать эту схему, используя суррогатный ключ:

user_friend_id
user_id
friend_id

Primary Key -> (user_friend_id)

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

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

ВКЛЮЧИТЬ СОРТИРОВКУ ПО ФИЛЬТРАЦИИ 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

Составной PK позволяет Mysql фильтровать и сортировать результаты, используя индекс; Mysql не нужно будет использовать временный файл или сортировку файлов для получения результатов. Без составного ключа это было бы невозможно и привело бы к очень неэффективному запросу.

Таким образом, составные ключи очень мощные и больше подходят для простой задачи: «Я хочу найти column_a, column_b, поэтому я буду использовать составные ключи. Для моей текущей схемы базы данных у меня столько же составных ключей, сколько и одиночных». ключи. Не забывайте об использовании составного ключа!

1 голос
/ 30 декабря 2013

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

Наиболее важными условиями использования индексов для GROUP BY являются что все столбцы GROUP BY ссылаются на атрибуты из одного индекса, и что индекс хранит свои ключи по порядку (например, это Индекс BTREE, а не индекс HASH)

1 голос
/ 01 декабря 2009

Для пространственного поиска вам необходим алгоритм R-Tree , который позволяет очень быстро выполнять поиск по географическим районам. Именно то, что вам нужно для этой работы.

В некоторые базы данных встроены пространственные индексы. Быстрый поиск в Google показывает, что в MySQL 5 они есть (и, глядя на ваш SQL, я предполагаю, что вы используете MySQL).

1 голос
/ 01 декабря 2009

Нет черного и белого, один размер подходит для всех ответов.

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

Вам нужно профилировать рабочую нагрузку вашего запроса, чтобы определить это.

Составной индекс вступает в действие, когда запросы могут полностью удовлетворяться из этого индекса.

ОБНОВЛЕНИЕ (в ответ на изменение опубликованного вопроса): если вы выбираете * из таблицы, может использоваться составной индекс, а может и нет. Вам нужно будет запустить EXPLAIN PLAN , чтобы быть уверенным.

0 голосов
/ 01 декабря 2009

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

Если вы будете искать точную широта / длинная пара, этот индекс, вероятно, будет иметь смысл. Но вы, вероятно, будете искать дома на определенном расстоянии от определенного места, поэтому ваши запросы будут выглядеть примерно так (см. source ):

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

и индекс, скорее всего, не поможет вообще. Для геопространственных запросов вам нужно что-то вроде this .

Обновление: с этим запросом:

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

Анализатор запросов может использовать индекс только для geolat, индекс только для geolng или, возможно, оба индекса. Я не думаю, что он будет использовать составной индекс. Но легко проверить каждую из этих перестановок на реальном наборе данных, а затем (а) посмотреть, что EXPLAIN говорит вам, и (б) измерить время, которое действительно занимает запрос.

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