#Что означает неисправность
ERROR 1062 (23000) «Duplicate entry ... for key» при регистрации может возникнуть, когда два запроса одновременно пытаются создать учётную запись с одинаковым адресом электронной почты или именем. Проверка SELECT перед INSERT не предотвращает гонку: обе операции могут увидеть отсутствие пользователя. Уникальный индекс должен оставаться последней защитой; приложение обязано корректно обработать его отказ.
#Возможные причины и признаки
| Возможная причина | Как узнать | Что делать |
|---|---|---|
| Два запроса одновременно создают одинаковый логинКнопка регистрации нажата дважды или запрос повторился после сетевого ожидания. | В журнале два близких по времени INSERT с одним нормализованным адресом. | Оставить UNIQUE и превратить 1062 в понятное сообщение о существующей записи. |
| Проверка существования отделена от вставкиSELECT COUNT(*) говорит 0, но пока приложение рассчитывает пароль, другое соединение уже вставляет тот же email. | Воспроизводится при параллельном запуске, а последовательная регистрация работает. | Обрабатывать исключение INSERT независимо от предварительного SELECT. |
| Уникален не тот индекс, который ожидалиUNIQUE может относиться к username, email или составному ключу, а collation меняет правила сравнения строк. | В сообщении названо имя индекса, отличное от предположения разработчика. | Посмотреть SHOW INDEX и уточнить продуктовые требования к уникальности. |
#Пошаговая диагностика и решение
- Запишите конкретный индекс и безопасно воспроизведите сценарийВ журнале найдите ERROR 1062 и имя индекса после
for key. Запишите время двух запросов, идентификатор операции и момент повторного клика без пароля пользователя. Не переписывайте обработчик ошибки как любое исключение базы: ошибка сетевого соединения или нехватки места требует другого ответа. Тестируйте на отдельной базе с фиктивными учётными записями. - Убедитесь, какая колонка действительно уникальнаВ MySQL Workbench откройте SQL Editor для тестовой базы или консоль mysql, выполните
SHOW CREATE TABLE users;иSHOW INDEX FROM users;для своей таблицы. Посмотрите имя UNIQUE и набор столбцов в порядке индекса. Если индекс составной, одно и то же значение email может сочетаться с разными tenant_id; сценарий регистрации должен учитывать эту область уникальности. - Проверьте нормализацию введённого значенияВ коде регистрации найдите обработку пробелов и регистра букв email или логина. Перед сравнением используйте согласованные правила приложения и корректную проверку формата; не применяйте произвольное Unicode-преобразование, которое меняет смысл идентификатора. Уточните collation колонки: регистронезависимая сортировка может считать две строки равными.
- Сохраните серверное ограничение UNIQUEНе снимайте UNIQUE ради того, чтобы два запроса успешно вставляли одинаковую запись. Проверьте, что нужный бизнес-идентификатор защищён индексом, а запросы выполняются в InnoDB. В тестовой таблице с UNIQUE вставьте одинаковое значение два раза и убедитесь, что второй INSERT даёт 1062; это защитный механизм, а не повод пересоздать таблицу.
- Перенесите окончательное решение на результат INSERTВ обработчике регистрации сначала можно выполнить SELECT ради удобного сообщения, но после INSERT всё равно перехватывайте ошибку с кодом 1062/SQLSTATE 23000. Если индекс соответствует email, покажите нейтральное сообщение о наличии учётной записи или предложите восстановление доступа; если конфликт по username, предложите другое имя. Не возвращайте пользователю текст SQL, хеш пароля и внутреннюю схему.
- Не подменяйте регистрацию опасным REPLACE или INSERT IGNORE
REPLACE INTOможет удалять и заново вставлять существующие строки, меняя ключи и связанные записи;INSERT IGNOREскрывает часть ошибок.INSERT ... ON DUPLICATE KEY UPDATEтакже требует явной бизнес-семантики и может обновить чужую учётную запись. Для обычной регистрации безопаснее отказ от второй вставки и явная обработка дубля. - Испытайте два параллельных запроса на тестовом стендеВ тестовом окружении отправьте два запроса на регистрацию одного фиктивного адреса одновременно через тестовый клиент или автотест. Ожидается одна созданная строка и один корректный отказ, а не две записи и не общий HTTP 500. Убедитесь, что письмо подтверждения и создание профиля инициируются только для фактически созданной записи после фиксации транзакции.
- Проверьте поведение при повторе запроса после сетевой ошибкиПосле успешной регистрации искусственно повторите тот же запрос в учебной системе. Приложение должно распознавать повтор и не создавать новую запись; при наличии отдельного механизма идемпотентности убедитесь, что он работает с тем же ключом запроса. Если результат первой попытки неизвестен из-за разрыва связи, сначала найдите запись по безопасному идентификатору, а не отправляйте новый INSERT вслепую.
#Команды для диагностики и проверки
SHOW CREATE TABLE users;SHOW INDEX FROM users;#Пример для отдельной тестовой базы или учебного проекта
CREATE TEMPORARY TABLE whelp_signups (id INT AUTO_INCREMENT PRIMARY KEY, email VARCHAR(255) NOT NULL, UNIQUE KEY uq_email(email)) ENGINE=InnoDB;
INSERT INTO whelp_signups(email) VALUES ('demo@example.test');
INSERT INTO whelp_signups(email) VALUES ('demo@example.test'); -- ожидается 1062
SELECT COUNT(*) AS signups FROM whelp_signups;#Проверка результата и дальнейшие действия
Успех: при одновременной регистрации единственная строка существует под одним идентификатором, второй запрос получает предусмотренный ответ, а связанных профилей и отправленных писем не становится два. Если 1062 возникает и при разных адресах, проверьте имя конфликтующего индекса и нормализацию значений. Откат изменений приложения — возврат предыдущего выпуска с сохранением UNIQUE.