Снова про AUTO_INCREMENT

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

Действительно, будем все хранить в оперативной памяти. Хотя, нет, ее маловато будет. Да и при неожиданном выключении все данные пропадут. Поставим ИБП. Нет, лучше 2. А еще лучше представим, что память у нас бесконечная и данные при выключении не теряет.

Теперь можно писать что-то типа такого:

Order order = new Order();
User user = new User();
order.creator = user;

И насоздавать хоть 10 таких заказов, хоть 20, хоть 100… Хм, что-то много я насоздавал, надо бы коллекцию какую-нибудь. А к ней поиск по полям. И язык запросов, чтобы все универсально было. Плюс индексы по этим полям для ускорения поиска.

Что у нас получилось? Получилась практически обычная база данных. За одним исключением – у объектов нет первичного ключа. Все объекты идентифицируются по адресу в адресном пространстве.

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

Это подводит к мысли, почему целочисленные ключи удобны в использовании. Причины не только в реализации систем управления БД. База данных – это адресное пространство для размещения объектов. А целочисленный ключ (ID) – это ссылка на объект.

Из этого следует, что auto_increment в пределах одной таблицы — это не совсем правильно. Каждая запись в БД должна иметь уникальный адрес. Получается как бы двухмерное адресное пространство – адреса растут в одном направлении, сами объекты в другом; размер одного объекта не влияет на адреса соседних объектов. При этом неважно, распределенная БД или нет, сколько в ней серверов, баз и таблиц. Это одно адресное пространство, и адресация должна быть однозначной.

Получается, теоретически запись можно найти по ключу без указания таблицы; или наоборот, можно найти саму таблицу, в которой находится запись с таким ключом. Можно даже разбить адресное пространство на диапазоны: например, до 1000000 системные таблицы, настройки, справочники; после 1000000 реальные данные. При переполнении можно добавлять к диапазону заранее определенную достаточно большую константу, или заранее настроить карту диапазонов.

Однако, ID не нужно относить к атрибутам самого объекта как элемента модели данных. Например:

int x = 2;

У переменной x есть значение 2 и адрес 0x123456. Но нельзя сказать, что адрес – это атрибут целочисленных значений. Также нельзя сказать, что ID – это атрибут объектов типа User и Order. Он служит просто для связи абстрактной модели и технической реализации.

Все ограничения естественного ключа нужно делать дополнительными техническими средствами, на уровне самой БД или приложения. На самом деле, я даже не могу представить ситуацию, в которой естественный первичный ключ является «естественным». Все варианты типа номер паспорта, телефона, ИНН – это искусственно введенная нумерация объектов, именно потому что нельзя выделить у них или их владельцев уникальный набор признаков. И использовать ее можно только для идентификации именно этих объектов – выданных паспортов, узлов телефонной сети, экономического субъекта, платящего налоги. В реальности всегда есть возможность существования дубликатов с одинаковыми свойствами, и набора разных свойств в разное время у одной сущности.

P.S.: Мои рассуждения носят теоретический характер, я не работал с базами с подобной структурой. Технически это сделать несложно – одна sequence на все таблицы. Если у кого-то есть такой опыт, просьба поделиться информацией в комментариях – плюсы, минусы, подводные камни.
Share post
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 15

    +5
    Чего вы пытаетесь добиться своим предложением?
      –2
      Добиться, пожалуй, ничего. В основном, хочу узнать, правильно я рассуждаю или нет.
        +5
        Рассуждаете вы неправильно.

        Во-первых, методологически. Нам не важно, как идентифицируются объекты; потому что объекты — это такая же абстракция, как и строчки в БД. Нам важно, как идентифицируются сущности из бизнес-модели. Причем именно сущности, а не значения (поэтому, скажем, пример с целым числом нам не интересен, поскольку число — это классический value type, у него не стоит проблемы идентификации). А у сущностей, в свою очередь, вполне себе есть проблема идентификации — какие-то сущности идентифицируются по формальным признакам (например, счет — по номеру), а какие-то — нет (например — человек). Когда мы преобразуем сущности в реализацию (объектную, документную, реляционную), мы тем или иным способом решаем проблему идентификации — где-то мы берем готовые признаки, где-то вводим искуственные. И внезапно выясняется, что тот простой способ идентификации объектов, который вы предлагаете — равенство и идентичность по ссылке — не работает, потому что для одной сущности внезапно может оказаться два объекта с разными адресами. Отсюда появляются компареры, identity maps и другие занимательные вещи. Поэтому нет, все объекты-то идентифицируются по адресу, но бизнесу плевать на объекты, ему нужны сущности.

        А во-вторых, технически. Что вы выигрываете от сквозного ключа? Окей, единый способ адресации. Как нам теперь понять, на каком шарде лежит объект? По диапазону? Дополнительная сущность. Какого типа объект? По диапазону? Еще одна сущность. А если у вас в системе есть короткоживущие объекты — они все равно будут поедать емкость из общего множества идентификаторов? А если у вас есть объекты с естественными идентификаторами — для них все равно заводить искусственный и поддерживать две уникальности? А если у вас есть связи, у которых «естественный» ключ — составной, с ними как поступать?

        Итого. Недостатков уйма, но где достоинства? Ощутимые, видимые достоинства, которые перевесят эти недостатки?
          0
          Да, бизнесу нужны сущности. И мы можем идентифицировать сущность по атрибутам, только если мы специально вводили для нее ключ в качестве атрибута — тот же номер счета. С этой точки зрения нет разницы, использовать для идентификации этот уникальный атрибут, или внутренний дополнительный.
          Кроме адресации, из плюсов мне пришли в голову примерно такие:
          — Без проблем можно перенести данные между таблицами при рефакторинге
          — Отображает последовательность создания объектов
          — Меньше ресурсов требуется на создание ID для новой записи (по сравнению с guid)

          Как нам теперь понять, на каком шарде лежит объект? По диапазону? Дополнительная сущность.

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

          Какого типа объект? По диапазону? Еще одна сущность.

          Ну таблицы-то никуда не деваются. Просто немного меняется способ генерации ключа.

          А если у вас в системе есть короткоживущие объекты — они все равно будут поедать емкость из общего множества идентификаторов?

          Да. Если на них бывают внешние ключи в других таблицах, хоть и ненадолго, то в этот момент они являются частью системы. А если не бывают, и если мы не хотим тратить на них идентификаторы, можно завести для них отдельную последовательность.
          Множество значений идентификаторов это небольшая проблема. Кроме того, если использовать GUID, то про них тоже можно сказать, что значения поедаются.

          А если у вас есть объекты с естественными идентификаторами — для них все равно заводить искусственный и поддерживать две уникальности?

          Да. Это ничем не отличается от составного ключа. Ну и технические причины есть, по числовому идентификатору искать быстрее, чем по 20-символьной строке с номером счета.

          А если у вас есть связи, у которых «естественный» ключ — составной, с ними как поступать?

          Здесь я пожалуй у вас спрошу, как у более опытного. Можете привести пару примеров? Мне не встречались случаи, где это действительно было нужно.
            0
            Ваши утверждения — сборник заблуждении. Просто хотя бы вспомните про индексы.
            Плюсы — надуманы.
            Например, для «отображения последовательности создания объектов», если это действительно нужно, заводят отдельное поле с датой-временем создания запись.
            Работал с подобными базами — преимуществ не увидел, имел проблемы при тестовых/нештатных «разворачивании бэкапов-переносов данных» — вечно забывали тот или иной счетчик передвинуть вперед на нужное значение.
              0
              Чем это помешает работе индексов?
              Дата-время не всегда имеет достаточную точность.
              Про счетчики — если у нас будет один централизованный счетчик на всю базу, то при вставке записи с большим id он будет и меняться соответственно. Как автоинкремент в таблице mysql.
              В любом случае, спасибо за информацию.
              0
              И мы можем идентифицировать сущность по атрибутам, только если мы специально вводили для нее ключ в качестве атрибута — тот же номер счета.

              Неправда. Мы можем идентифицировать сущность по атрибутам в том случае, если это возможно с точки зрения бизнеса. Номер счета — это не какой-то атрибут, который ввели мы, это атрибут, который есть в бизнесе безо всякого вмешательства с нашей стороны.

              С этой точки зрения нет разницы, использовать для идентификации этот уникальный атрибут, или внутренний дополнительный.

              Вообще-то, разница большая. Натуральный и искусственный ключи отличаются по поведению и ограничениям.

              Кроме адресации, из плюсов мне пришли в голову примерно такие:
              — Без проблем можно перенести данные между таблицами при рефакторинге

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

              — Отображает последовательность создания объектов

              Во-первых, только в том случае, если вы никогда не заполняете пустоты. А во-вторых, зачем вам это надо в масштабе всей БД?

              — Меньше ресурсов требуется на создание ID для новой записи (по сравнению с guid)

              Во-первых, это относится не к глобальный vs локальный, а к автоинкремент vs guid. А во-вторых это банально неправда. Посудите сам: вот у вас есть система со сквозной автоинкрементной идентификацией. Чтобы получить следующий идентификатор, вам надо заблокировать то место, где вы храните последнее значение, увеличить его, использовать, разблокировать. Все в рамках транзакции. Это означает, что все ваши операции вставки выстроены в строго последовательную очередь, требующую обращения к одному бутылочному горлышку. Это меньше ресурсов? Поверьте, нет.

              Ну таблицы-то никуда не деваются. Просто немного меняется способ генерации ключа.

              А как понять, из какой таблицы читать, получив на входе только ключ? И как понять, какая будет форма результата?

              А если не бывают, и если мы не хотим тратить на них идентификаторы, можно завести для них отдельную последовательность.

              После этого вы теряете свою единую адресацию со всеми ее преимуществами. Вы не можете так сделать.

              Ну и технические причины есть, по числовому идентификатору искать быстрее, чем по 20-символьной строке с номером счета.

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

              А если у вас есть связи, у которых «естественный» ключ — составной, с ними как поступать?

              Здесь я пожалуй у вас спрошу, как у более опытного. Можете привести пару примеров?

              Типовой пример: книга написана автором, связь многие-ко-многим, но один автор не может написать одну книгу дважды. В таблице связей нет собственного искусственного ключа, только «естественный» составной — BookId, AuthorId.
                0
                Да, наверно я был неправ. Спасибо за разъяснение.
        +1
          0
          Не совсем. Я имел в виду абстракцию над такими вещами, чтобы ее можно было использовать и для больших таблиц, и в распределенной базе.
          +1
          Хм. Тут как-то все свалено в кучу.
          Я знаком со свойством AUTO_INCREMENT только в реляционных базах данных. В этих базах данных отсутствует понятие «объект», поэтому и говорить о каком-то «адресе объекта» бессмысленно. Естественно, сами данные где-то хранятся и у этого места хранения есть «физический адрес». Некоторые СУБД дают API к этому адресу (например, в Oracle есть псевдоколонка rowid). Но считать этот адрес идентификатором объекта — нельзя.
          А в объектных базах данных глобально-уникальные идентификаторов используются вовсю и да, он вынесен за пределы модели данных. Так что тут у вас ничего нового. Правда я не встречал реализаций, когда этот идентификатор целочисленный и возрастающий (аналог AUTO_INCREMENT), но это тоже вполне объяснимо с учетом того, что объекты в таких системах появляются у клиентов и обеспечение синхронизации последовательностей требует никому ненужных дополнительных затрат.
            0
            Справедливости ради, у автора промелькнула мысль про диапазоны адресов, правда, он их с таблицами связал, а не с клиентами.
              0
              Но считать этот адрес идентификатором объекта — нельзя

              Да, я знаю. Поэтому у меня и появилась мысль о более абстрактном адресе.
              0
              Поздравляю вы изобрели GUID
              ru.wikipedia.org/wiki/GUID
                –1
                Ну у него тоже свои недостатки есть. Поэтому я подумал о другом подходе. Ну и в целом, попытался взглянуть на идентификаторы с другой точки зрения.

              Only users with full accounts can post comments. Log in, please.