- Kiedy należy używać indeksu złożonego w bazie danych?
- Jakie są konsekwencje wydajności przy użyciu indeksu złożonego)?
- Dlaczego powinienem używać indeksu złożonego?
Na przykład mam homes
stół:
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 ;
Czy ma sens stosowanie indeksu złożonego dla obu geolat
i geolng
, takich jak:
Wymieniam:
KEY `geolat` (`geolat`),
KEY `geolng` (`geolng`),
z:
KEY `geolat_geolng` (`geolat`, `geolng`)
W takim razie:
- Czemu?
- Jakie są konsekwencje wydajności przy użyciu wskaźnika złożonego)?
AKTUALIZACJA:
Ponieważ wiele osób stwierdziło, że jest to całkowicie zależne od zapytań, które wykonuję, poniżej znajduje się najczęściej wykonywane zapytanie:
SELECT * FROM homes
WHERE geolat BETWEEN ??? AND ???
AND geolng BETWEEN ??? AND ???
AKTUALIZACJA 2:
Z następującym schematem bazy danych:
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 ;
Używając następującego 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 zwraca:
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
Nie bardzo rozumiem, jak czytać polecenie EXPLAIN. Czy to wygląda dobrze, czy źle. W tej chwili NIE używam indeksu złożonego dla geolat i geolng. Czy powinienem być?
WHERE geolat BETWEEN ??? AND ??? AND geolng BETWEEN ??? AND ???
. Zatrzyma się po pierwszym polu. Odpowiedź z „Przepełnienia pytań” wyjaśnia dlaczego.Wyobraź sobie, że masz następujące trzy zapytania:
Zapytanie I:
SELECT * FROM homes WHERE `geolat`=42.9 AND `geolng`=36.4
Zapytanie II:
SELECT * FROM homes WHERE `geolat`=42.9
Zapytanie III:
SELECT * FROM homes WHERE `geolng`=36.4
Jeśli masz oddzielny indeks na kolumnę, wszystkie trzy zapytania używają indeksów. W MySQL, jeśli masz indeks złożony (
geolat
,geolng
), tylko zapytanie I i zapytanie II (które używa pierwszej części indeksu złożonego) używa indeksów. W tym przypadku zapytanie III wymaga pełnego przeszukania tabeli.Na kilku kolumnach indeksy części instrukcji, jest jasno wyjaśnione w jaki sposób działa wiele indeksów kolumn, więc nie chcę wpisywać ręcznie.
Ze strony podręcznika MySQL :
Jeśli używasz oddzielnego indeksu dla kolumn geolat i geolng, masz w tabeli dwa różne indeksy, które możesz przeszukiwać niezależnie.
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
Jeśli używasz indeksu złożonego, masz tylko jeden indeks dla obu kolumn:
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 to względny numer rekordu (dla uproszczenia można powiedzieć ID). Pierwsze dwa indeksy zostały wygenerowane oddzielnie, a trzeci indeks jest złożony. Jak widać, możesz wyszukiwać na podstawie geolng na złożonym, ponieważ jest indeksowany przez geolat, jednak możliwe jest wyszukiwanie według geolat lub „geolat AND geolng” (ponieważ geolng jest indeksem drugiego poziomu).
Również spojrzeć w jaki sposób MySQL używa indeksów sekcja obsługi.
źródło
BETWEEN
), dalsze pola indeksu nie są brane pod uwagę! Więc indeks złożony nie jest lepszy.Może istnieć błędne przekonanie na temat tego, co robi indeks złożony. Wiele osób uważa, że indeks złożony może być użyty do optymalizacji zapytania wyszukiwania, o ile
where
klauzula obejmuje indeksowane kolumny, w twoim przypadkugeolat
igeolng
. Zagłębmy się głębiej:Uważam, że dane dotyczące współrzędnych domów byłyby przypadkowymi miejscami dziesiętnymi:
Ponieważ
geolat
igeolng
wartości prawie się nie powtarzają. Złożony indeksgeolat
igeolng
wyglądałby mniej więcej tak:Dlatego druga kolumna indeksu złożonego jest w zasadzie bezużyteczna ! Szybkość zapytania z indeksem złożonym prawdopodobnie będzie podobna do indeksu tylko dla
geolat
kolumny.Jak wspomniał Will, MySQL zapewnia obsługę rozszerzeń przestrzennych . Punkt przestrzenny jest przechowywany w jednej kolumnie zamiast w dwóch oddzielnych
lat
lng
kolumnach. Do takiej kolumny można zastosować indeks przestrzenny. Jednak skuteczność mogłaby być przeceniona na podstawie moich osobistych doświadczeń. Możliwe, że indeks przestrzenny nie rozwiązuje problemu dwuwymiarowego, a jedynie przyspiesza wyszukiwanie przy użyciu R-drzew z podziałem kwadratowym .Kompromis polega na tym, że punkt przestrzenny zużywa znacznie więcej pamięci, ponieważ do przechowywania współrzędnych używa ośmiobajtowych liczb o podwójnej precyzji. Popraw mnie, jeśli się mylę.
źródło
Indeksy złożone są przydatne w przypadku
Indeks złożony nie może obsługiwać dwóch zakresów. Omawiam to dalej w mojej indeksowej książce kucharskiej .
Znajdź najbliższy - jeśli naprawdę chodzi o optymalizację
wtedy żaden indeks nie może obsłużyć obu wymiarów.
Zamiast tego trzeba „myśleć nieszablonowo”. Jeśli jeden wymiar jest implementowany poprzez partycjonowanie, a drugi przez ostrożne wybieranie
PRIMARY KEY
, można uzyskać znacznie lepszą wydajność dla bardzo dużych tabel wyszukiwania lat / lng. Mój blog latlng zawiera szczegółowe informacje o tym, jak zaimplementować funkcję „znajdź najbliższy” na świecie. Zawiera kod.Są
PARTITIONs
to pasma szerokości geograficznych.PRIMARY KEY
Celowo rozpoczyna się od długości tak, że użyteczne wiersze mogą być w tym samym bloku. Przechowywana rutyna organizuje niechlujny kod do wykonywaniaorder by... limit...
i powiększania „kwadratu” wokół celu, dopóki nie będzie wystarczającej liczby kawiarni (lub czegokolwiek). Zajmuje się również obliczeniami po ortodromie oraz obsługą linii danych i biegunów.Jeszcze
Napisałem innego bloga; porównuje 5 sposobów wyszukiwania lat / lng: http://mysql.rjweb.org/doc.php/latlng#representation_choices (odwołuje się do linku podanego powyżej jako jednego z 5). Jednym z innych sposobów jest: i wskazuje, że są one optymalne dla konkretnego przypadku :
Oznacza to, że ważne jest posiadanie obu kolumn w dwóch indeksach i brak indeksów jednokolumnowych w geolat i geolng.
źródło
Indeksy złożone mają bardzo duże możliwości, ponieważ:
ZACHOWAJ INTEGRALNOŚĆ STRUKTURY
Indeksy złożone to nie tylko inny typ indeksów; mogą zapewnić KONIECZNĄ strukturę tabeli, wymuszając integralność jako klucz podstawowy.
Mysql's Innodb obsługuje klastrowanie, a poniższy przykład ilustruje, dlaczego indeks złożony może być konieczny.
Aby utworzyć przyjaciół tabel (czyli do sieci społecznościowej) musimy 2 kolumny:
user_id, friend_id
.Struktura stołu
Z racji tego, klucz podstawowy (PK) jest unikalny i tworząc złożony PK, Innodb automatycznie sprawdzi, czy
user_id, friend_id
po dodaniu nowego rekordu nie ma duplikatów . Jest to oczekiwane zachowanie, ponieważ na przykład żaden użytkownik nie powinien mieć więcej niż 1 rekord (łącze relacji)friend_id = 2
.Bez złożonej PK możemy utworzyć ten schemat przy użyciu klucza zastępczego:
Teraz za każdym razem, gdy dodawany jest nowy rekord, będziemy musieli sprawdzić, czy poprzedni rekord z kombinacją
user_id, friend_id
już nie istnieje.Jako taki, indeks złożony może wymusić integralność struktury.
WŁĄCZ SORTOWANIE NA FILTROWANYM IDENTYFIKATORZE
Bardzo często zestaw rekordów jest sortowany według czasu publikacji (znacznik czasu lub data i godzina). Zwykle oznacza to wysyłanie na podany identyfikator. Oto przykład
Tabela User_Wall_Posts (pomyśl o postach na ścianie Facebooka)
Chcemy przeszukiwać i znajdować wszystkie posty dla
user_id = 10
i sortować komentarze wedługtimestamp
(data).ZAPYTANIE SQL
SELECT * FROM User_Wall_Posts WHERE user_id = 10 ORDER BY timestamp DES
Złożona PK umożliwia Mysql filtrowanie i sortowanie wyników przy użyciu indeksu; MySQL nie będzie musiał używać pliku tymczasowego ani sortowania plików do pobrania wyników. Bez klucza złożonego nie byłoby to możliwe i spowodowałoby bardzo nieefektywne zapytanie.
W związku z tym klucze złożone są bardzo potężne i pasują bardziej niż prosty problem „Chcę wyszukać,
column_a, column_b
więc użyję kluczy złożonych. W moim obecnym schemacie bazy danych mam tyle samo kluczy złożonych, co pojedynczych kluczy. Nie przeocz zastosowanie klucza złożonego!źródło
PRIMARY KEY
Wymusza integralności ze względu na toUNIQUE
, bycie złożonym jest drugorzędne.Do wyszukiwania przestrzennego potrzebny jest algorytm R-Tree , który umożliwia bardzo szybkie przeszukiwanie obszarów geograficznych. Dokładnie to, czego potrzebujesz do tej pracy.
Niektóre bazy danych mają wbudowane indeksy przestrzenne. Szybkie wyszukiwanie w Google pokazuje, że MySQL 5 je ma (patrząc na twój SQL domyślam się, że używasz MySQL).
źródło
Indeks złożony może być przydatny, gdy chcesz zoptymalizować
group by
klauzulę (sprawdź ten artykuł http://dev.mysql.com/doc/refman/5.0/en/group-by-optimization.html ). Proszę uważać:źródło
GROUP BY
nie została wymieniona.GROUP BY
nie została wymieniona w PO.Nie ma czarno-białego, jeden rozmiar pasuje do wszystkich.
Należy użyć indeksu złożonego (lub wielokolumnowego), jeśli obciążenie pracą zapytania przyniesie korzyści.
Aby to ustalić, musisz sprofilować obciążenie pracą zapytania.
Indeks złożony wchodzi w grę, gdy zapytania mogą być w całości spełnione z tego indeksu: co oznacza, że wszystkie kolumny wymagane przez zapytanie są objęte (pokryte) przez indeks.
UPDATE (w odpowiedzi na edycję wysłanego pytania): Jeśli wybierasz * z tabeli, można użyć indeksu złożonego, ale nie może. Aby mieć pewność, musisz uruchomić EXPLAIN PLAN .
źródło
Jestem z @Mitch, zależy wyłącznie od twoich zapytań. Na szczęście możesz tworzyć i usuwać indeksy w dowolnym momencie oraz możesz dołączyć słowo kluczowe EXPLAIN do swoich zapytań, aby sprawdzić, czy analizator zapytań używa indeksów.
Jeśli szukasz dokładnej pary długich i długich, ten indeks prawdopodobnie miałby sens. Ale prawdopodobnie będziesz szukać domów w pewnej odległości od określonego miejsca, więc Twoje zapytania będą wyglądać mniej więcej tak (patrz źródło ):
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
a indeks najprawdopodobniej nie będzie w ogóle pomocny. Dla zapytań geoprzestrzennych, trzeba coś jak ten .
Aktualizacja: za pomocą tego zapytania:
SELECT * FROM homes WHERE geolat BETWEEN ??? AND ??? AND geolng BETWEEN ??? AND ???
Analizator zapytań mógłby użyć indeksu samego geolat lub indeksu samego geolng, lub ewentualnie obu indeksów. Nie sądzę, by użyłby indeksu złożonego. Ale łatwo jest wypróbować każdą z tych permutacji na rzeczywistym zbiorze danych, a następnie (a) zobaczyć, co powie Ci EXPLAIN i (b) zmierzyć czas, jaki naprawdę zajmuje zapytanie.
źródło