Какой индекс лучше использовать для поиска по уникальному полю в базе данных
Что проверяют
Понимаете ли вы, что для уникального поля индекс обычно уже есть, и чем уникальный индекс полезен помимо запрета дублей. Следом спрашивают, как обработать нарушение уникальности в Go и почему схема «сначала SELECT, потом INSERT» не работает.
Как отвечать
Ответ: уникальный B-tree индекс, и чаще всего создавать его руками не нужно.
- ограничение создаёт индекс само:
PRIMARY KEYиUNIQUEв PostgreSQL и MySQL автоматически строят уникальный B-tree. Второй индекс на то же поле только замедлит запись; - почему не хэш: хэш-индекс в PostgreSQL тоже умеет равенство, но уникальным быть не может;
- польза для планировщика: база знает, что строк не больше одной, и останавливает поиск на первой найденной;
- покрывающий индекс:
CREATE UNIQUE INDEX ON users (email) INCLUDE (id)позволяет ответить из индекса без обращения к таблице (Index Only Scan); - нормализация: email, логины и промокоды обычно уникальны без учёта регистра, это индекс по
lower(email)или типcitext; - NULL: по умолчанию несколько
NULLв уникальной колонке допустимы, с PostgreSQL 15 это меняется черезNULLS NOT DISTINCT.
В MySQL InnoDB первичный ключ кластерный, поэтому поиск по вторичному уникальному индексу идёт в два шага: сначала к первичному ключу, потом к строке.
В Go полагайтесь на ограничение базы. С pgx ошибку разбирают через errors.As в *pgconn.PgError и сверяют Code == "23505", а для сценария «вставить, если нет» используют INSERT ... ON CONFLICT (email) DO NOTHING RETURNING id.
Где спотыкаются
Проверяют уникальность в приложении: сначала SELECT, затем INSERT. Без ограничения в базе два параллельных запроса оба увидят пустоту и оба вставят.
Создают UNIQUE и отдельно CREATE INDEX на ту же колонку. Получаются два одинаковых индекса, и оба обновляются при каждой записи.
Сравнивают текст ошибки вместо кода. Сообщение зависит от локали сервера и версии, а код 23505 стабилен.