Чому MySQL не має хеш-індексів на MyISAM або InnoDB?


35

У мене є програма, яка вибере лише рівність, і я вважаю, що я повинен використовувати хеш-індекс над btree-індексом. На жаль, на MyISAM чи InnoDB не підтримуються хеш-індекси. Що з цим?


2
Mysql також не підтримує функціональні індекси, растрові індекси тощо. Просто тому, що це mysql ;-)

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

1
@ Алекс: Б'юсь об заклад, що причина - "лінь" та "бюрократія", але давайте чекатимемо відповідей))


До кінця своєї відповіді я додав хороший алгоритм HASH з книги MySQL High Performance.
RolandoMySQLDBA

Відповіді:


16

Багато баз даних не підтримують індекси на основі хеша на всіх .

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

Таким чином, щоб бути загалом корисним (тобто, як правило, краще за альтернативу), індекс потрібно періодично перебудовувати, коли дані зростають (і скорочуються), що може призвести до значних переривчастих витрат. Зазвичай це добре з таблицями на основі пам'яті, оскільки відновлення, ймовірно, буде досить швидким (оскільки дані завжди будуть в оперативній пам’яті і, швидше за все, не будуть масовими в будь-якому випадку), але відновлення великого індексу на диску - це дуже важка операція (а IIRC mySQL не підтримує перебудову індексів в реальному часі, тому утримує блокування таблиці під час операції).

Отже, хеш-індекси використовуються в таблицях пам'яті, оскільки там вони, як правило, кращі виконавці, але таблиці на основі диска не підтримують їх, оскільки це може бути шкодою для продуктивності, а не бонусом. Там немає нічого , щоб зупинити хеш - індекси, які виділяються для таблиць на основі диска, звичайно, не сумніваються , що деякі бази даних зробити підтримують функцію, але по- видимому , вони не реалізовані в ISAM / InnoDB таблиця як супровідники не вважає художнім варто додати (як додатковий код писати та підтримувати не вартує вигоди за тих кількох обставин, що це суттєво зміниться). Можливо, якщо ви категорично не погоджуєтесь, ви могли б поговорити з ними і зробити гарний випадок для реалізації функції.

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


Чи є якийсь спосіб дозволити повторне хешування (відновлення) робити поруч, не замикаючи всю таблицю?
Печер'є

@Pacerier: не те, що я знаю з MySQL (хоча вони могли додати функцію з моменту останнього її використання, тому перевірте документацію). Навіть там, де СУБД підтримує створення / відновлення індексу в Інтернеті, це не стандартний варіант. Що заблокується, буде залежати від: деякі будуть тримати блокування запису на столі, інші транзакції не затримуються, якщо вони лише читають, деякі DMBS знімають повний замок таблиці. Якщо вам потрібна перебудова в Інтернеті, перевірте документацію кожної СУБД, перш ніж вибрати, яку використовувати.
Девід Спіллетт

Зазвичай перебудова потрібна лише тоді, коли довжина даних збільшується вдвічі. Чи їм справді доводиться турбуватися про подвоєння довжини даних щохвилини? (зазвичай це трапляється дуже рідко, коли база даних зростає досить великою, щоб це викликало занепокоєння)
SOFe,

6

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

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

Примітка: Тестування показало, що хеш-індекси PostgreSQL не краще, ніж індекси B-дерева, а розмір індексу та час складання для хеш-індексів набагато гірші. Крім того, операції з індексом хешу в даний час не реєструються WAL, тому хеш-індекси, можливо, знадобиться перебудувати за допомогою REINDEX після збоїв у базі даних. З цієї причини в даний час використання хеш-індексу не рекомендується. Аналогічно, індекси R-дерева, схоже, не мають жодних переваг у порівнянні з еквівалентними операціями індексів GiST. Як і хеш-індекси, вони не мають WAL-журналу та можуть потребувати перевстановлення після збою бази даних. Хоча проблеми з хеш-індексами можуть бути вирішені врешті-решт, цілком ймовірно, що тип індексу R-tree буде видалений у майбутньому випуску. Користувачам рекомендується мігрувати програми, які використовують індекси R-дерева, на індекси GiST.

Знову ж таки, це (застаріла версія) PostgreSQL-специфічного, але це має натякати, що "природний" тип індексу не обов'язково повинен забезпечити оптимальну ефективність.


5

Ось щось цікаве:

Відповідно до книги Посібник з вивчення сертифікації MySQL 5.0 , сторінка 433, розділ 29.5.1

Двигун MEMORY використовує HASH за алгоритмом індексації за замовчуванням.

Для сміху я спробував створити таблицю InnoDB та таблицю MyISAM з первинним ключем за допомогою HASH в 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 не скаржився.

ОНОВЛЕННЯ

Погані новини !!! Я використовував показ покажчиків. Він говорить, що індекс BTREE.

CREATE INDEX Синтаксис MySQL Сторінка стверджує , що тільки ПАМ'ЯТЬ і двигуни зберігання NDB може вмістити HASH INDEX.

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)

Деякі люди запропонували дотримуватися ідеї на Сторінках 102-105 книги " Високопродуктивний MySQL: оптимізація, резервне копіювання, реплікація та багато іншого ", щоб імітувати алгоритм хешування.

Сторінка 105 містить цей швидкий і брудний алгоритм, який мені подобається:

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

Складіть стовпчик для цього в будь-якій таблиці та індексуйте це значення.

Спробувати !!!


5
Перш ніж використовувати техніку псевдо-хеш-індексу у виробництві, проведіть деякий аналіз її ефективності. Для великих рядків це може мати велике значення, але ви все одно переходите до індексу дерева в кінці кінців, і у вас є додаткові порівняння, щоб знайти потрібний рядок із знайдених відповідних хешу, тому для малих значень обчислюючи хеш-значення зберігати їх просто не варто. Це насправді зовсім не хеш-індекс, ви просто скорочуєте роботу, виконуючи ходьбу по дереву (оскільки кожне порівняння розглядає менше байт, наприклад, порівнюючи 8 байт INT, замість рядків x00 байт).
Девід Спіллетт

@David Spillett У цьому я цілком повинен погодитися з тобою. Інші стратегії індексації також пропонуються в тій же книзі в Розділі 11 "Стратегії індексації для високої ефективності". Як додатковий стимул до моєї відповіді, книга фактично згадує про використання кластерного індексу, який зберігає рядок та індекс BTree в одній структурі. Це може пришвидшити скорочену вами роботу. На жаль, обручі, через які ви повинні перестрибнути, що ви вже згадали, дещо неминучі. Незважаючи на це, на ваш коментар +1, сер !!! Справді, +1 і для вашої відповіді.
RolandoMySQLDBA

@RolandoMySQLDBA Чи можете ви детальніше зупинитися на деталі щодо "спеціального хешування", останній абзац, здається, не дає багато підказки ...
Pacerier

2

BTree не набагато повільніше, ніж Hash для однорядного пошуку. Оскільки BTree пропонує дуже ефективні запити про діапазон, чому б не турбуватися чим-небудь, крім BTree.

MySQL робить дуже хорошу роботу з кешування блоків BTree, тому запит на основі BTree рідко доводиться робити введення-виведення, який є найбільшим споживачем часу в будь-якому запиті.

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