Префіксний індекс індексує не всю рядкову колонку, а лише перші N символів:
CREATE INDEX urls_path_idx ON urls (path(50));
Навіщо:
- ліміт довжини ключа - для формату рядка
DYNAMICце 3072 байти.VARCHAR(1000)вutf8mb4важить до 4000 байтів і повністю в індекс не поміститься; TEXTіBLOBбез префікса індексувати взагалі не можна;- розмір індексу - менший індекс краще вміщується в буферний пул.
Як обрати довжину префікса. Префікс має бути досить довгим, щоб розрізняти значення майже так само добре, як повна колонка:
SELECT COUNT(DISTINCT path) / COUNT(*) AS full_selectivity,
COUNT(DISTINCT LEFT(path, 20)) / COUNT(*) AS p20,
COUNT(DISTINCT LEFT(path, 50)) / COUNT(*) AS p50,
COUNT(DISTINCT LEFT(path, 100)) / COUNT(*) AS p100
FROM urls;
Беруть найменшу довжину, селективність якої близька до повної. Для URL з однаковим початком (https://example.com/blog/...) префікс має бути довшим, ніж здається.
Обмеження префіксних індексів:
- не можуть бути покривними - повне значення все одно читається з таблиці;
- не допомагають
ORDER BYіGROUP BYза колонкою; - унікальний префіксний індекс перевіряє унікальність лише префікса: два різні URL з однаковими першими 50 символами не вставляться.
Альтернатива для пошуку за точним збігом довгого рядка - індекс на хеші:
ALTER TABLE urls
ADD COLUMN path_hash BINARY(16) AS (UNHEX(MD5(path))) STORED,
ADD UNIQUE INDEX (path_hash);
SELECT * FROM urls WHERE path_hash = UNHEX(MD5(?)) AND path = ?;
Індекс компактний (16 байтів), працює й для унікальності повних значень.
Історична довідка про Laravel. Schema::defaultStringLength(191) з'явився через старий ліміт 767 байтів (формат COMPACT): 191 × 4 = 764. На сучасних MySQL цей ліміт - 3072 байти, і VARCHAR(255) з індексом працює без хитрощів.