В InnoDB таблиця і є індексом: рядки зберігаються в B-дереві, впорядкованому за первинним ключем. Це дерево називають кластерним індексом. Окремої «купи» рядків, як у PostgreSQL, немає.
Як InnoDB обирає кластерний індекс:
- первинний ключ (
PRIMARY KEY); - якщо його немає - перший
UNIQUE-індекс, у якого всі колонкиNOT NULL; - якщо немає й такого - прихований індекс
GEN_CLUST_INDEXза внутрішнім 6-байтовим ідентифікатором рядка, невидимим для запитів.
Вторинні індекси (усі інші) зберігають не адресу рядка, а значення первинного ключа:
CREATE TABLE orders (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT UNSIGNED NOT NULL,
total DECIMAL(10, 2) NOT NULL,
INDEX (user_id) -- фактично зберігає пари (user_id, id)
);
SELECT total FROM orders WHERE user_id = 7;
-- 1) пошук у індексі user_id -> отримано id
-- 2) пошук у кластерному індексі за id -> отримано total
Наслідки для практики:
- Пошук за первинним ключем найдешевший - одне проходження дерева, і рядок уже на місці.
- Довгий первинний ключ роздуває всі індекси, бо його копія є в кожному вторинному.
BIGINT(8 байтів) протиCHAR(36)для UUID (36 байтів) - відчутна різниця на великій таблиці. - Випадковий первинний ключ шкодить вставці. Автоінкремент дописує рядки в кінець дерева. Випадкові UUIDv4 вставляють у довільні сторінки, викликають їх розщеплення й фрагментацію.
- Вторинний індекс «безкоштовно» покриває первинний ключ.
SELECT id FROM orders WHERE user_id = 7читає лише індекс, без другого кроку. - Діапазон за первинним ключем дешевий:
WHERE id BETWEEN 1000 AND 2000читає сусідні сторінки.
Тому в Laravel $table->id() (BIGINT UNSIGNED AUTO_INCREMENT) - вдалий вибір за замовчуванням для InnoDB, а для UUID краще впорядковані за часом значення (UUIDv7, HasUuids).