Pull to refresh

Comments 29

Можно половину гуида делать инкрементной, а половину случайной. Так и сортируемость будет и гарантия непересекаемости
Алгоритм, который использует UuidCreateSequential() гарантирует непересекаемость (в предыдущей статье описано). Эта же функция используется SQL сервером для получения результата NEWSEQUENTIALID()
>> Так и сортируемость будет…
Сортировка по первичному ключу? Не учите людей плохому.
Отвертку можно использовать, чтобы забивать гвозди? В принципе можно, но люди предпочитают для этого использовать молоток. Роль первичного ключа не сортировку обеспечивать, а однозначную идентификацию запись в БД. Если вам нужна сортировка, то для этого надо использовать другие поля.
Вы так и не ответили на вопрос, что плохого в кластеризации по первичному ключу, но сообщили о полезном свойстве первичного ключа — «однозначная идентификация».

Мне, например, тоже непонятно, чем плох первичный ключ с кластеризацией. Статья, по моему, как-раз про это — сравнение двух скоростных методов — GUID и инкремент по кластерному ключу.
Ох, ты. Из-за какой-то мелочи еще и в карму минусов поднакидали. Молодцы!
Похоже, ответа на любопытный вопрос не будет…
Понимание приходит с опытом.
Ничем. Но это самый последний вариант для кластерного ключа, когда остальные уже были отброшены (вместе с вариантом «не использовать его в принципе»).
У Guid есть еще несколько преимуществ:
1. Повышенная устойчивость к перебору, что очень хорошо сказывается на безопасности приложения;
2. Уникальность в пределах планеты очень сильно облегчает объединение данных из различных источников.

У огромного количества проектов нет необходимости в безумной оптимизации скорости выполнения запросов к БД и в этом случае предпочтение отдается удобству взаимодействия с БД. В реальности какая разница получите вы ответ через 200 мс или через 220 мс? Если же ваш проект действительно высоконагруженный и критичен к минимальным задержкам, то вам не то, что от Guid придется отказаться, но и от Entity Framework.
Про преимущества GUID я писал в первой статье, в том числе и про эти. В тех случаях, когда этих преимуществ не требуется, можно обойтись целочисленным ключом.

А что касается безумной оптимизации — то я тут полностью согласен, чаще всего она не нужна )
обойтись целочисленным ключом.
Как будто вот по умолчанию такой ключ, а можно еще «добавить» GUID как дополнительную опцию. Вон в MongoDb гуиды рулят вовсю. Позиционирует себя как база данных для высоконагруженных систем.

«Безумная оптимизация», «какая разница», «действительно высоконагруженный проект» — это все абсолютизмы. В любом проекте есть перформанс вопросы. Я иногда захожу на сайт it событий в моем городе и он тормозит и бесит меня. Хотя там врят ли больше тысячи человек зарегистрировано.
Любые перформанс вопросы можно выразить в цифрах — на этом и в статье упор. Другое дело, что часто оказывается, что в деньгах это совсем немного. Если например увеличить ресурсы сервера базы. Да и перформанс ресерч не показывал еще при мне основные проблемы именно на этом уровне. Всегда оправданней было увеличить производительность где то в другом месте.

Но потенциально это может быть перформанс дырой для ряда проектов. И дело тут не в объеме нагрузки как таковой. А в относительном количестве запросов по идентификатору к общему количеству запросов. Если это отношение велико, то эта перформанс проблема может оказаться значимой в не зависимости от того 10 пользователей ходят на сайт или 10 миллионов.
Хотя вообще то вопрос целое число\гуид решается для всех сущностей базы обычно. Поэтому фактически это значимо для любых запросов. В этом случае можно рассматривать вероятность проблемы в зависимости от нагрузки.
Вообще этот вопрос неплохо было бы проинвестигировать отдельно. При каких именно условиях такая проблема может стать значительной. В статье именно этот вопрос как то не особенно раскрыт.
Часто решается перевести только часть сущностей базы на гуиды, например, только ту, которая участвует в синхронизации с другой базой, чтобы гуиды работали по прямому назначению, обеспечивая глобальную уникальность, а, например, различные справочники остаются с целочисленными (а то и короткими текстовыми) ключами и, если используются в обеих системах, то либо приводятся к одному, либо между ними делается маппинг. А иногда используются и два ключа (один первичный, второй — просто уникальный индекс).
Уникальность в пределах планеты очень сильно облегчает объединение данных из различных источников.

Не то чтобы уникальность, просто очень маленькая (даже мизерная) вероятность коллизии.
Нет у гуид повышенной устойчивости к перебору. Особенно у Sequential. Если вам нужна криптографическая стойкость, нужно использовать другие алгоритмы. А следующий гуид вполне можно предсказать.
Есть способ генерации ключевого поля без гуида, но при этом полностью получая преимущества гуида.
Этот способ заключается в том, что каждый источник генерации автоинкремента имеет собственный идентификатор, который известен всегда и статичен для данного клиента (статичность — непринципиальна, в общем-то, одно и то же устройство/экземпляр клиента может получать разовые идентификаторы сессии для каждого соединения)

Проще говоря, мы имеем набор методов генерации автоинкремента на основе ключа клиента или ключа разовой сессии клиента из множества конкурентных.

Что нам необходимо? Чтобы сгенерированный клиентом ключ гарантированно не пересекался со сгенерированным в это же время ключом на другом клиенте. То есть мы не хотим коллизии.

Решение проблемы в том, что ключ имеет форму комбинации/смеси (ключ клиента/сессии, известное последнее значение ключа, которое уже занято)

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

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

Почему процесс генерации бывает медленным? Потому что необходимо произвести определенный протокол обмена данными между клиентом и сервером для того, чтобы и клиент и сервер знали необходимый ключ

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

Когда клиент генерирует ограниченное количество записей за короткое время, нет никакой проблемы генерировать необходимые ключи на сервере и возвращать их клиенту в ответ на операцию записи (для того, чтобы клиент знал, какие данные он только что записал).

На практике мы использовали GUID в своих системах, но я к ним отношусь крайне скептически и считаю довольно расточительным механизмом.

Я бы доработал GUID как универсальный механизм как минимум до генерации серии GUID необходимой емкости, в которой легко догенерировать клиентскую часть.

Впрочем, может быть, кто-то знает, как подобное сделать в существующих библиотеках?
Странно, что никто не упомянул алгоритм генерации ObjectId из MongoDB:
ObjectId is a 12-byte BSON type, constructed using:

a 4-byte value representing the seconds since the Unix epoch,
a 3-byte machine identifier,
a 2-byte process id, and
a 3-byte counter, starting with a random value.
Упомянуть-то можно — просто еще один алгоритм генерации последовательного GUID. Принципиально нового он ничего не вносит. Реализовывать аналог с тем же алгоритмом и размером для того же MS SQL или Oracle может оказаться не оправдывающей себя задачей.
Строго говоря, в статье обсуждаются суррогатные ключи. И, по хорошему, использовать их в качестве первичных нехорошо, потому что ведёт к нарушению семантики данных. И конечно же, это повсеместно игнорируется
На форуме пользователь по имени Вася Пупкин отправил другому пользователю по имени Вася Пупкин сообщение «Привет, тезка», причем умудрился нажать кнопку «Отправить» 2 раза подряд за одну секунду. Какие семантически значимые свойства сущностей «Пользователь» и «Сообщение» вы бы выбрали в качестве их естественных ключей?
Например, никнейм (для «несерьёзных» проектов) / номер телефона (для «серьёзных» проектов) и timestamp.

В нормальных базах у timestamp разрешение в 1 микросекунду, www.postgresql.org/docs/9.4/static/datatype-datetime.html
Номер телефона не является натуральным идентификатором сущности «человек». Собственно, ничто не является. Никнейм пользователя является таким идентификатором только в том случае, если система не позволяет их менять.
Папиллярные узоры, код ДНК — вроде как являются. Насчёт никнейма — в принципе, если система гарантирует уникальность, то тоже является идентификатором, правда не сущности «человек», а сущности «пользователь», даже если можно их менять, при условии, что нам не нужно поддерживать внешнюю ссылочную целостность. Но в целом, да — как только дело касается физических лиц, очевидное решение — суррогатный ключ, по крайней мере пока каждому при рождении не будет присваиваться уникальный код. Но это тоже суррогатный ключ, просто внешней системы :)
>> В нормальных базах у timestamp разрешение в 1 микросекунду
И что это меняет? Мне неизвестна ни одна СУБД, которая бы гарантировала, что в один момент времени в БД не вставят несколько записей.
Во-первых, unique constraint на поле с таймстампом это вполне гарантирует.
Во-вторых, это автоматически реализует антифлуд.
Во-первых, unique constraint на поле с таймстампом это вполне гарантирует.

Они гарантируют, что в базе не будет двух записей с одинаковым первичным ключом (что и так гарантируется по определению первичного ключа), но не гарантирует отсутствие коллизий при сохранении, а значит пользователи могут получать ошибки типа «извините, Вася Пупкин, но ваша запись не может быть сохранена, поскольку другой Вася Пупкин в эту микросекунду уже сохранил другую». Вообще у сообщений нет естественного первичного ключа. Даже полное сообщения (все заголовки и тело) с таймстампом с точностью до наносекунды таким ключом не является — в общем случае они могут дублироваться.

А с точки зрения «автоматического антифлуда» особой разницы нет может пользователь генерировать в микросекунду одно сообщение или десять. Ну и далеко не во всех системах требуется антифлуд, а где требуется лучше его реализовывать отдельным явным конфигуриремым фильтром.
И, по хорошему, использовать их в качестве первичных нехорошо, потому что ведёт к нарушению семантики данных.

Какой-то авторитетный источник для подкрепления этого заявления найдется?
Sign up to leave a comment.

Articles