it-swarm.dev

Dlaczego MySQL nie ma indeksów skrótu w MyISAM lub InnoDB?

Mam aplikację, która wybierze tylko na zasadzie równości, i sądzę, że powinienem użyć indeksu skrótu zamiast indeksu btree. Ku mojemu przerażeniu indeksy skrótów nie są obsługiwane w MyISAM ani InnoDB. O co chodzi?

36
Alex

Wiele baz danych nie obsługuje indeksów opartych na haszach w ogóle.

Aby tablica skrótów była wydajna, musisz znać liczbę wierszy, które prawdopodobnie będą obecne, w przeciwnym razie podstawowa tabela skrótów będzie o wiele za duża (wiele pustych wpisów, marnowanie miejsca i potencjalnie We/Wy dysku) lub zbyt mała, co oznacza, że często stosuje się pośrednie (możliwe, że wiele poziomów pośrednich, lub nawet gorzej, jeśli implementacja skrótu jest jednopoziomowa, możesz w końcu przeprowadzić wyszukiwanie liniowe na sporej liczbie rekordów), w którym to momencie rzeczy prawdopodobnie nie są bardziej wydajne niż oparte na drzewie indeksuj mimo to.

Aby być ogólnie użytecznym (tj. Zwykle lepszym niż alternatywa), indeks musi być od czasu do czasu przebudowywany, gdy dane rosną (i kurczą się), co może powodować znaczne przejściowe obciążenie. Zwykle jest to w porządku w przypadku tabel opartych na pamięci, ponieważ odbudowa prawdopodobnie będzie dość szybka (ponieważ dane zawsze będą w RAM i w żadnym wypadku nie będą ogromne), ale przebudowanie dużego indeksu na dysku jest bardzo ciężką operacją (a IIRC mySQL nie obsługuje przebudowywania indeksu na żywo, więc blokuje tabelę podczas operacji).

W związku z tym indeksy skrótów są używane w tabelach pamięci, ponieważ są one generalnie lepiej wydajne, ale tabele oparte na dyskach nie obsługują ich, ponieważ mogą być szkodliwe dla wydajności, a nie premii. Oczywiście nic nie stoi na przeszkodzie, aby indeksy skrótów były dostępne dla tabel opartych na dyskach, bez wątpienia niektóre bazy danych do obsługują tę funkcję, ale prawdopodobnie nie są zaimplementowane w tabelach ISAM/InnoDB, ponieważ opiekunowie nie biorą pod uwagę funkcja warta dodania (ponieważ dodatkowy kod do napisania i utrzymania nie jest wart korzyści w tych kilku okolicznościach, które mają znaczącą różnicę). Być może, jeśli zdecydowanie się nie zgadzasz, możesz z nimi porozmawiać i uzasadnić wdrożenie tej funkcji.

Jeśli indeksujesz duże ciągi, wdrożenie własnego pseudo-skrótu (przez przechowywanie skrótu wartości oraz wartości rzeczywistej i indeksowanie z kolumną) może działać, ale jest to zdecydowanie bardziej wydajne w przypadku dużych ciągów (gdzie obliczenie wartości skrótu i ​​przeszukanie indeksu drzewa za pomocą tej wartości zawsze będzie prawdopodobnie szybsze niż po prostu przeszukanie indeksu drzewa przy użyciu większych wartości do porównania, a zastosowana dodatkowa pamięć nie będzie znacząca), więc wykonaj analizę wydajności przed wdrożeniem to w produkcji.

16
David Spillett

W pokrewnej notatce dyskusja na temat typów indeksów z dokumentów PostgreSQL może okazać się interesująca. Nie jest już obecny w najnowszych wersjach dokumentów (ze względu na kolejne optymalizacje, biorę to), ale wynos może być podobny do MySQL (i powód, dla którego indeksy skrótów są używane tylko dla tabel sterty):

http://www.postgresql.org/docs/8.1/static/indexes-types.html

Uwaga: Testy wykazały, że indeksy mieszające PostgreSQL nie działają lepiej niż indeksy B-drzewa, a rozmiar indeksu i czas kompilacji dla indeksów mieszających są znacznie gorsze. Ponadto operacje indeksów skrótów nie są obecnie rejestrowane w WAL, więc indeksy skrótów mogą wymagać odbudowania za pomocą REINDEX po awarii bazy danych. Z tych powodów używanie indeksu skrótu jest obecnie odradzane. Podobnie, indeksy R-drzewa nie wydają się mieć żadnych korzyści wydajnościowych w porównaniu do równoważnych operacji indeksów Gist. Podobnie jak indeksy skrótów, nie są one rejestrowane w WAL i mogą wymagać ponownego indeksowania po awarii bazy danych. Podczas gdy problemy z indeksami skrótu mogą zostać ostatecznie rozwiązane, prawdopodobne jest, że typ indeksu R-drzewa zostanie wycofany w przyszłej wersji. Zachęcamy użytkowników do migracji aplikacji korzystających z indeksów R-drzewa do indeksów Gist.

Ponownie, jest to (przestarzała wersja) specyficzna dla PostgreSQL, ale powinna sugerować, że „naturalny” typ indeksu niekoniecznie zapewnia optymalną wydajność.

6

Oto coś interesującego:

Zgodnie z książką MySQL 5.0 Certification Study Guide , strona 433, sekcja 29.5.1

Mechanizm MEMORY używa domyślnie algorytmu indeksowania HASH.

Dla śmiechu próbowałem utworzyć tabelę InnoDB i tabelę MyISAM z kluczem podstawowym za pomocą HASH w MySQL 5.5.12

mysql> use test
Database changed
mysql> create table rolando (num int not null, primary key (num) using hash);
Query OK, 0 rows affected (0.11 sec)

mysql> show create table rolando\G
*************************** 1. row ***************************
       Table: rolando
Create Table: CREATE TABLE `rolando` (
  `num` int(11) NOT NULL,
  PRIMARY KEY (`num`) USING HASH
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)

mysql> create table rolando2 (num int not null, primary key (num) using hash) engine=MyISAM;
Query OK, 0 rows affected (0.05 sec)

mysql> show create table rolando2\G
*************************** 1. row ***************************
       Table: rolando2
Create Table: CREATE TABLE `rolando2` (
  `num` int(11) NOT NULL,
  PRIMARY KEY (`num`) USING HASH
) ENGINE=MyISAM DEFAULT CHARSET=latin1
1 row in set (0.00 sec)

MySQL nie narzekał.

AKTUALIZACJA

Złe wieści !!! Użyłem SHOW INDEXES From. Mówi, że indeks to BTREE.

CREATE INDEX syntax Page MySQL Page stwierdza, że ​​tylko silniki pamięci MEMORY i NDB mogą obsługiwać INDEKS HASH.

mysql> show indexes from rolando;
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table   | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| rolando |          0 | PRIMARY  |            1 | num         | A         |           0 |     NULL | NULL   |      | BTREE      |         |               |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
1 row in set (0.00 sec)

mysql> show indexes from rolando2;
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table    | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| rolando2 |          0 | PRIMARY  |            1 | num         | A         |           0 |     NULL | NULL   |      | BTREE      |         |               |
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
1 row in set (0.00 sec)

mysql> create table rolando3 (num int not null, primary key (num)) ENGINE=MEMORY;
Query OK, 0 rows affected (0.03 sec)

mysql> show create table rolando3\G
*************************** 1. row ***************************
       Table: rolando3
Create Table: CREATE TABLE `rolando3` (
  `num` int(11) NOT NULL,
  PRIMARY KEY (`num`)
) ENGINE=MEMORY DEFAULT CHARSET=latin1
1 row in set (0.00 sec)

mysql> show indexes from rolando3;
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table    | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| rolando3 |          0 | PRIMARY  |            1 | num         | NULL      |           0 |     NULL | NULL   |      | HASH       |         |               |
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
1 row in set (0.00 sec)

Niektórzy sugerowali, że podążą za tym pomysłem na stronach 102-105 książki „ High Performance MySQL: Optimizations, Backup, Replication and More ”, aby emulować algorytm skrótu.

Strona 105 zawiera ten szybki i brudny algorytm, który lubię:

SELECT CONV(RIGHT(MD5('whatever value you want'),16),16,10) AS HASH64;

Utwórz kolumnę do tego w dowolnej tabeli i zindeksuj tę wartość.

Spróbuj !!!

5
RolandoMySQLDBA

BTree nie jest dużo wolniejszy niż Hash dla wyszukiwania w jednym wierszu. Ponieważ BTree zapewnia bardzo wydajne zapytania o zasięg, po co zawracać sobie głowę czymkolwiek innym niż BTree.

MySQL bardzo dobrze buforuje bloki BTree, więc zapytanie oparte na BTree rzadko musi wykonywać operacje We/Wy, które są największymi konsumentami czasu w każdym zapytaniu.

2
Rick James