@VladMystetskyi
Full-stack web developer

Хорошая ли идея использовать в качестве ID (первичного ключа) мд5 хеш?

Собственно проектирую базу данных, сущностями в которой есть изображения. Хочу избежать проблем с дубликатами имен, дырками после удалений (если использовать просто инкремент айди) и везде в качестве айди использовать хешы. Вероятность коллизий вроде бы совсем не высокая на предполагаемом количестве объектов в базе.


Какие могут быть подводные камни?
  • Вопрос задан
  • 8122 просмотра
Пригласить эксперта
Ответы на вопрос 14
Мой учитель по Java мне говорил: «Каждый раз, когда вы используете hash как id, вы убиваете котёнка.»
Ответ написан
Комментировать
shushu
@shushu
Не очень хорошая идея. Инт всегда лучше для примари поля.
Я бы использовал int( или bigint) с автоинкрементоv, для внутренних целей, а наружу уже передавал бы md5, предварительно записанный в эту же таблицу отдельным полем, либо md5(id+salt)
Ответ написан
@Jazzist
Да легко. Не ведитесь на старые учения — современные движки БД, и даже MyISAM работают с текстовыми индексами не существенно медленнее, чем с целочисленными.

Если есть сомнения — просто попробуйте оценить реальную нагрузку. Скорее всего, это место узким не будет. Помните о принципе «вреда преждевременной оптимизации», который гласит: «Преждевременная оптимизация — вредна.»

Я бы усомнился в целесообразности самого решения, но не в плане ресурсоемкости, а в плане удобства разработки. Получить идентификатор свежевставленной записи, сопроводить ее внешними ключами, отработать исключения — все это делается как бы на автопилоте. Если действительно не требуются дополнительные функциональности, для которых необходимы дополнительные ключи (которые в свою очередь так-же можно обслужить во внешней таблице, как родительские....)
Ответ написан
md5-хэш в качестве id следует использовать только, если он заменяет составной ключ (несколько ключевых полей). В остальных случаях мы используем int (bigint) с unsigned и auto_increment.
Ответ написан
Комментировать
@Ghostwriter
В вашей задаче, вам не нужен ни MD5, ни автоинкрементный INT, как советовали выше.
Используйте в качестве первичного ключа бинарное поле и вписывайте в него 16-байтные UUID4-последовательности, а пользователям можете отдавать их в виде 32-символьной HEX-строки.

Про накладные расходы. По сравнению с int64, каким должен быть автоинкрементный счётчик, размер поля окажется больше всего на 8 байт (в 2 раза), что для вашего случая — совсем не проблема.
Убедитесь лишь, что ваши индексы всегда умещается целиком в физической памяти, а не хранятся частично или полностью в свопе на диске. Природа UUID такова, что он генерирует равномерно распределенные значения, а значит, поиск по такому индексу будет подвержен эффекту «случайного поиска» (random lookup). И если ваш индекс хотя бы частично хранится на диске, то это может привести к многочисленным random seeks и очень медленной
Ответ написан
Zorkus
@Zorkus
Просто вот это — дырками после удалений (если использовать просто инкремент айди) — а в чем тут проблема? Ну будут дырки и будут. Это же суррогатные ключи.

Да, если уйти глубоко в стек, можно поговорить по clustering factor на индексах и прочем ;) но это уже мелочи.
Повторю, я бы использовал внутри СУБД именно этот подход.
Ответ написан
Комментировать
Если «Мд5 строить все равно придется», он будет заведомо уникальным (то есть логика не допускает добавления двух картинок с одним хэшем), то почему нет (при условии что индексы и основной, и связанных таблиц будут помещаться в памяти, если важно быстродействие). В любом случае, насколько я понимаю, хэш будет считаться только при попытке добавления картинки в базу, а количество выборок по ид будет много больше числа таких попыток.

Вот только я бы посоветовал хорошо подумать, а действительно ли вам нужна уникальность хэша, не может ли это требование потом измениться. Или, скажем, решите сменить алгоритм хэша — изменение отдельного уникального поля куда менее дешевая операция, чем изменение основного ключа и всех индексов связанных таблиц.

Плюс, если я правильно понял идею (что-то вроде фотохостинга, хэш картинки используется для урлов) могут быть интересные ситуации типа: один пользователь добавил картинку, получил её новый урл, потом второй добавил её же, система засекла дубликат и выдала уже существующий хэш. Затем первый пользователь удалил картинку, система засекла, что есть ещё ссылки и удалила только ссылку из альбомов первого пользователя, не удаляя саму картинку — в итоге первый пользователь может увидеть, что несмотря на удаление картинка осталась доступна по старому урлу. Кому-то может всё равно, а кто-то может поднять крик про личные данные и т. п.
Ответ написан
Zorkus
@Zorkus
Какие веские причины не использовать autogenerated surrogate key?
Ответ написан
opium
@opium
Просто люблю качественно работать
Не иди по пути идиотизма.
Ответ написан
xiWera
@xiWera
А каков размер поля по которому строится md5-хэш? И что там за данные?
Ответ написан
alexkbs
@alexkbs
Используйте SHA1, как в Git.
Ответ написан
Комментировать
GearHead
@GearHead
Fullstack разработчик и предприниматель
а какая БД? у MongoDB на этот случай прекрасное решение в качестве ID: 12-ти байтное число, которое формируется исходя из узла (при шардинге), timestamp'а, process ID и всего трёх байт автоинкремента (подробнее здесь). Возможно есть подобное для РБД.
Ответ написан
Комментировать
@ComodoHacker
Помимо уже упомянутых подводных камней (размеры таблиц и индексов, производительность) и других по мелочи, не назвали самый главный — использование естественного ключа для PK вместо суррогатного. На эту тему написано много умных статей и глупых холиваров на форумах, я тут расписывать не буду, просто приведу одну ситуацию:

«Ой, не ту картинку загрузили! Надо срочно исправить!… Ой, это было месяц назад, на нее уже куча ссылок в куче таблиц… Ой, там такие констрейнты, простой UPDATE не прокатит… Ой, часть данных уже в архиве, что с ними делать?..»
Ответ написан
Комментировать
kapitansky
@kapitansky
Cогласен с учителем Norraxx (17 мая в 23:58 «Мой учитель по Java мне говорил: «Каждый раз, когда вы используете hash как id, вы убиваете котёнка.»») и VitaZheltyakov (md5-хэш в качестве id следует использовать только, если он заменяет составной ключ (несколько ключевых полей). В остальных случаях мы используем int (bigint) с unsigned и auto_increment). Причём только в том случае если суммарная сложность поиска по нескольким ключевым полям будет выше, чем у хеша…

Чем меньше ключ — тем быстрее происходит поиск по нему, поэтому всегда надо стремиться минимизировать его размер.

Поиск по числовому ключу зачастую много быстрее (бывает, что и до 4х раз быстрее, возможно и ещё быстрее) нежели поиск по числу той же длинны… и это верно не только для реляционных СУБД, но и для подавляющего большинства языков программирования (вполне вероятно, что потребуется искать в множестве, полученном ранее из БД).

Исходя из вашей задачи, для начала, в качестве типа PK я бы выбрал unsigned mediumint (16,777,215 — 1 записей)- его всегда можно будет расширить до int, а затем и bigint (если, конечно, это потребуется).
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы