Ответ
Да, в большинстве реляционных баз данных можно создавать составные (composite) индексы, которые включают в себя несколько колонок. Это позволяет значительно ускорить выполнение запросов, фильтрующих данные сразу по нескольким полям.
Зачем это нужно?
Составной индекс эффективен для запросов вида WHERE column1 = 'value1' AND column2 = 'value2'. Без него базе данных пришлось бы сначала фильтровать по одному индексу, а затем сканировать полученные результаты по второму условию.
Пример на чистом SQL:
CREATE INDEX idx_name_email ON users (name, email);
Пример в Go с использованием GORM: В GORM составные индексы удобно определять с помощью тегов структуры. Важно дать одинаковое имя индексу в тегах у всех полей, которые должны в него войти.
type User struct {
ID uint
Name string `gorm:"index:idx_name_email"`
Email string `gorm:"index:idx_name_email"`
Age int
}
Ключевые моменты:
- Порядок полей в индексе критически важен. Запрос
WHERE Name = ?сможет использовать индекс(Name, Email), а запросWHERE Email = ?— как правило, нет. Запрос должен использовать поля в том порядке, в котором они определены в индексе (левый префикс). - Компромисс производительности. Составные индексы ускоряют чтение (SELECT), но могут незначительно замедлять операции записи (INSERT, UPDATE), так как базе данных нужно обновлять и индексную структуру.
Ответ 18+ 🔞
А, ну ты про индексы составные, да? Вот эта магия, когда база данных не тупит, как пень, а летает, как ракета, когда ты ищешь по нескольким полям сразу. Да, блядь, это реально работает, не выдумка.
Смотри, представь себе таблицу users. Ты ищешь Васю Пупкина с почтой vasya@mail.ru. Без индекса база будет чесать репу и перебирать всё подряд, как дурак. А если создать индекс сразу по двум полям — (name, email) — то она сразу, блядь, как хитрая жопа, прыгнет прямо на нужную запись. Всё потому, что она заранее всё отсортировала и знает, где что лежит.
Как это в коде выглядит, ёпта? Вот, смотри, на чистом SQL это делается просто:
CREATE INDEX idx_name_email ON users (name, email);
А в Go, с этим нашим любимым GORM'ом, ещё проще. Прямо в структуре прописываешь. Главное — дать одинаковое имя индексу у всех полей, которые должны в него войти, а то он не поймёт, что они связаны, и наделает кучу отдельных индексов, дурак.
type User struct {
ID uint
Name string `gorm:"index:idx_name_email"` // Вот тут имя одно
Email string `gorm:"index:idx_name_email"` // И тут то же самое, блядь!
Age int
}
Но, слушай сюда, самое важное, что все хуёво понимают:
Порядок полей в индексе — это пиздец как важно! Это не просто список, это, блядь, иерархия! Индекс (name, email) — это как телефонный справочник: сначала по фамилии, а потом уже по имени внутри фамилии.
- Запрос
WHERE name = 'Вася'— охуенно, использует индекс. - Запрос
WHERE name = 'Вася' AND email = '...'— идеально, использует индекс на полную катушку. - А вот запрос
WHERE email = '...'— нихуя! Он этот индекс, скорее всего, проигнорирует, потому что искать почту без имени — это как искать иголку в стоге сена, начиная с середины стога. Бесполезно, блядь.
И да, компромисс, куда же без него. Такие индексы — они как спортивная машина: читает (SELECT) — овердохуища быстро, но каждая запись (INSERT, UPDATE) становится чуть дороже, потому что движку базы приходится не только данные писать, но и эту свою хитрую индексную структуру обновлять. Но обычно оно того стоит, если запросы частые.
В общем, думай головой, где у тебя узкие места, и применяй эту штуку точечно. Не навешивай индексы на всё подряд, а то получишь обратный эффект, и всё встанет колом.