MySQL – optymalizacja wydajności, indeksy

  Bazy danych, Webhosting

Aby w sposób zrozumiały dla każdego, przedstawić czym jest i jak działa indeks bazy danych, pozwolimy sobie na użycie analogii do życia codziennego. Wyobraź sobie, że znajdujesz się w ogromnej bibliotece i chcesz znaleźć książkę o konkretnym tytule (który znasz). Masz 2 możliwości. Możesz przejrzeć całą bibliotekę, wszystkie regały, czytając wszystkie tytuły książek jeden po drugim. Trwałoby to zapewne bardzo dużo czasu.

Drugą możliwością jest skorzystanie z katalogu bibliotecznego, którym jest zazwyczaj skrzynka z szufladami, w których znajdują się karty katalogowe z podstawowymi informacjami o wszystkich książkach. Karty katalogowe ułożone są alfabetycznie według haseł tytułowych. Sięgniesz więc do szuflady oznaczonej pierwszą literą tytułu poszukiwanej pozycji, i dzięki alfabetycznemu ułożeniu bardzo szybko znajdziesz jej kartę katalogową. Na jej podstawie dowiesz się w którym regale, a na której półce znajduje się dana książka. Dla pewności podejdziesz do tego regału, aby skontrolować czy książka rzeczywiście się tam znajduje. Druga metoda jest niewątpliwie bardzo skuteczna, a jeśli czytelnicy często wyszukują książki wg tytułu lub autora, to na pewno warto w tym celu stworzyć dla nich rejestry.

To samo dotyczy bazy danych.

Czym jest indeks bazy danych

Indeksy to struktury danych, które umożliwiają szybkie wyszukiwanie rekordów wg indeksowanych pozycji. Dla przykładu, mamy tabelę, w której każda pozycja jest jednoznacznie identyfikowana przez konkretne ID (np. cały numer), a my chcemy odnaleźć pozycję o konkretnym numerze, np. za pomocą następującego polecenia:

SELECT * FROM ksiazki WHERE ID=1234

Jeśli w kolumnie ID nie widnieje żaden indeks, MySQL musi przejrzeć cały plik z danymi, rekord po rekordzie, odczytując ID w każdym z nich. Jest to dość skomplikowane, ponieważ oprócz ID, rekord może zawierać inne obszerne dane, a MySQL musi odczytać je wszystkie, a następnie przeanalizować. Ponadto dane zamieszczone w pliku są zwykle nieuporządkowane (np. uszeregowane w takiej kolejności, w jakiej zostały dodane do bazy danych), przez co może dojść do sytuacji, że przy wyszukiwaniu natrafisz na wartość wyższą niż poszukiwana, przez co pozostała część pliku nie zostanie przeszukana.

W tych przypadkach konieczne jest utworzenie indeksu dla elementu ID. Oznacza to, że w odrębnym pliku zostanie zapisana odpowiednio uszeregowana lista wszystkich ID występujących w tabeli (w tym przypadku konkretnie tzw. B-drzewo, co w języku matematyczno-informatycznym oznacza drzewo wyszukiwania, zachowujące minimalną głębokość), wraz z linkiem do lokalizacji rekordu o tym ID w pliku danych. Indeks wydaje się być znacznie mniejszy (ponieważ zawiera jedynie ID oraz wskaźnik do pliku danych), a ponadto jest bezpośrednio przystosowany do szybkiego wyszukiwania.

Indeksy nie służą jedynie do wyszukiwania konkretnej wartości jednej pozycji, ale mogą być też użyte do wyszukiwania interwału.

Indeksy mieszane

Oczywiście możliwe jest utworzenie oddzielnego indeksu dla każdej z pozycji. Istnieje jednak inna możliwość. Jeśli często wyszukujesz rekordy wg wielu kryteriów jednocześnie, możesz utworzyć jeden indeks nad tysiącami kolumn tabeli jednocześnie. Przykładowo, jeśli często wyszukujesz książki wg roku wydania i typu, zapytanie SELECT mogłoby wyglądać następująco:

SELECT * FROM ksiazki WHERE rok_wydania=2007 And typ='monografia'

Przy wyszukiwaniu zostanie użyty tylko jeden indeks. Gdybyśmy mieli dwa odrębne indeksy nad kolumnami rok_wydania i typ, zostałby użyty tylko jeden z nich (wybór pomiędzy nimi zależy od implementacji, zazwyczaj prognozuje się, który będzie bardziej pomocny). Jeśli zostanie użyty indeks dla roku_wydania, wszystkie rekordy z rokiem wydania 2007 zostaną szybko wyszukane w indeksie. Wszystkie te rekordy będą musiały zostać wczytane, a następnie jeden po drugim skontrolowane, dla ustalenia, które z nich są monografiami. Z indeksu takiej informacji nie uzyskamy. Nawet jeśli zaoszczędziliśmy sobie wczytywania wszystkich rekordów tabeli z dysku, i ograniczyliśmy się tylko do książek z 2007, to nadal nie jest to, co chcieliśmy osiągnąć. Jeśli bardzo często wyszukujemy wg tych kryteriów, powinniśmy znaleźć lepsze rozwiązanie.

Teraz przychodzi kolej na indeks mieszany umieszczony nad kolumną rok_wydania i typ (w tej kolejności). W ramach indeksu mieszanego wyszukuje się wg obu kryteriów jednocześnie, tj. wyszukuje się wg pierwszej kolumny, z nawiązaniem na kolejny indeks (B-drzewo), który odpowiada wyszukiwanej wartości pierwszego atrybutu, a następnie wyszukiwany jest drugi atrybut. Indeks ten jest bardziej skomplikowany i mniej wydajny, ale nadal o rząd wielkości szybszy, niż odczyt danych z dysku.

W przypadku zastosowania indeksu mieszanego zgodnie z powyższym przykładem wyszukiwanie nie zawsze musi przebiegać wg wszystkich kolumn, które są w nim uwzględnione. Możliwe jest użycie jedynie „lewej” części indeksu. Indeks mieszany rok_wydania+typ może być użyty do wyszukiwania np. wg samego roku wydania, wtedy też zostaną uwzględnione wszystkie typy. Wyszukiwanie odwrotne, tj. wyszukiwanie przy użyciu tego indeksu wg typu, z pominięciem roku_wydania, nie jest możliwe.

Indeksy pełnotekstowe

Specjalny rozdział stanowią indeksy pełnotekstowe. Pozwalają one na przeszukiwanie ogromnych plików danych wg słów kluczowych. Przy utworzeniu indeksu pełnotekstowego analizie zostają poddane słowa i ich liczebność, wg, której określane jest ich znaczenie. Im częściej dane słowo występuje w tekście, tym większe jest prawdopodobieństwo, że dotyczy on tematu, który jest wyrażany przez dane słowo. Indeksy pełnotekstowe nie są niestety tak proste, dużo zależy od konkretnej implementacji i innych ulepszeń.

Mamy więc indeks, zawierający słowa i ich znaczenie w poszczególnych rekordach. Podczas wyszukiwania wprowadzamy jedno lub kilka słów kluczowych, które zostaną znalezione w indeksie – wyniki są zazwyczaj sortowane malejąco.

Indeksy pełnotekstowe są dostępne w MySQL tylko przy użyciu tabeli typu MyISAM.

Indeks podstawowy i unikalny

Indeksem unikatowym oznacza się indeks wykluczający wielokrotne wystąpienie pojedynczej wartości w danej kolumnie. Po pierwsze, daje nam pewność, że nie dojdzie do przypadkowego, kilkukrotnego dodania pojedynczego rekordu z unikatowymi danymi do bazy danych, po drugie system bazy danych może dzięki temu lepiej optymalizować strukturę indeksów, ponieważ sytuacja, gdzie jedna wartość pozycji w indeksie będzie wskazywała na kilka rekordów z pliku danych, nie będzie miała miejsca.

Indeksem podstawowym określa się unikalny indeks nad kolumną tabeli, którego wartość jednoznacznie określa wiersz (rekord) i zawsze ma jakąś wartość (nie może to być NULL).

Jak prawidłowo wybierać indeksy

Dobór odpowiedniego indeksu nad tabelą może być nie lada wyzwaniem. Niektórzy mogliby pomyśleć, że najlepiej byłoby zaindeksować wszystkie kolumny. W takim przypadku cała problematyka została źle zrozumiana. Im większe indeksy, tym obszerniejszy plik, w którym są zapisane, tym więcej odczytywanych z dysku bloków danych z indeksami, a tym samym więcej przestrzeni niezbędnej do ich przechowywania. Nie jest to jednak jedyny problem wielkich indeksów. Należy pamiętać również o tym, że w przypadku dodania rekordów do tabeli (np. poleceniem INSERT INTO), lub przy ich zmianie/usunięciu (UPDATE, DELETE), musi to zostać uwzględnione we wszystkich indeksach (sortowanie, zmiana, usunięcie), co może okazać się dość skomplikowaną operacją (zdarza się, że indeks musi zostać całkowicie nadpisany).

Przy doborze indeksów warto przemyśleć kilka kwestii:

  • jaki będzie stosunek zapytań do aktualizacji – w przypadku bardzo dużej liczby zapytań, a małej liczby aktualizacji, możemy dodać więcej indeksów, ponieważ zmiany nie będą wprowadzane aż tak często
  • które z kolumn będą najczęściej przeszukiwane, lub jakie ich kombinacje będą używane
  • czy możliwe jest przeformułowanie zapytań SQL w taki sposób, aby ułatwić tworzenie indeksów

Należy także pamiętać, że indeksy dla wartości całkowitych będą znacznie szybsze i mniej obszerne niż indeksy nad polami tekstowymi (dodatkowa komplikacja wynika z tego, że ciągi tekstowe mogą mieć zmienną długość, w związku z czym bardziej skomplikowane jest skonstruowanie odpowiedniego B-drzewa).

Dobór indeksów jest kwestią indywidualną i wymaga uprzedniego przeanalizowania sposobów wykorzystania bazy danych.