Неизменность в дизайне баз данных

26

Одним из элементов «Эффективной Java» Джошуа Блоха является представление о том, что классы должны обеспечивать как можно меньшую мутацию экземпляров, а желательно - вообще.

Часто данные объекта сохраняются в базе данных некоторой формы. Это привело меня к мысли об идее неизменности в базе данных, особенно для тех таблиц, которые представляют одну сущность в более крупной системе.

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

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

create table myObj (id integer, ...other_data... not null);
create table myObjSuppliment (id integer, myObjId integer, ...more_data... not null);

Надеюсь, очевидно, что эти имена не дословно, а просто для демонстрации идеи.

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

Эд Каррел
источник
7
Я чувствую, что это решение без проблем ... Вы должны обновлять, а не создавать сложные адаптации, чтобы избежать обновления.
Fosco
Я думаю, что это больше было связано с тем, чтобы иметь в виду интуитивно понятное представление о решении и желать, чтобы им руководило как можно больше людей, и в процессе осознания того, что это может быть не лучшим решением для моей проблемы. Я могу открыть другой вопрос с проблемой, если я не могу найти его в другом месте.
Эд Каррел
1
Могут быть веские причины избегать обновлений в базах данных. Однако, когда эти причины все же возникают, это скорее проблема оптимизации, и поэтому не следует делать это без доказательства наличия проблемы.
dietbuddha
6
Я думаю, что есть сильный аргумент в пользу неизменности в базах данных. Это решает много проблем. Я думаю, что негативные комментарии не пришли от непредубежденных людей. Обновления на месте являются причиной многих проблем. Я бы сказал, что у нас все это задом наперед. Обновления на месте - это устаревшее решение проблемы, которой больше не существует. Хранение дешево. Зачем это делать? Сколько систем БД имеют журналы аудита, системы управления версиями, потребность в распределенной репликации, которая, как мы все знаем, требует способности поддерживать задержку для масштабирования. Неизменность решает все это.
циррус
@Fosco Некоторые системы абсолютно необходимы, чтобы никогда не удалять данные (включая использование UPDATE). Как медицинские записи доктора.
Изката

Ответы:

25

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

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

Базы данных делают то же самое, используя атомарные транзакции : когда вы запускаете транзакцию, она записывает все новые обновления в новое место на диске. Когда вы завершаете транзакцию, он меняет указатель на диске, где находятся новые обновления - что он делает в короткий промежуток времени, в течение которого другие процессы не могут его коснуться.

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

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

Рей Миясака
источник
Спасибо за ответ. Эта перспектива была как раз тем, что мне нужно было понять, что моя интуиция сбивала с толку попытки объединить пару разных идей в единый шаблон.
Эд Каррел
8
Это немного больше, чем безразличие. Я чаще всего вижу аргумент в пользу неизменности в контексте ООП: неизменные объекты требуют, чтобы вы проверяли их состояние только один раз в конструкторе. Если они изменчивы, то каждый метод, который может изменить свое состояние, должен также проверять, что полученное состояние все еще допустимо, что может значительно усложнить класс. Этот аргумент потенциально применим и к базам данных, но он намного слабее, поскольку правила проверки БД имеют тенденцию быть декларативными, а не процедурными, поэтому их не нужно дублировать для каждого запроса.
Дейв Шерохман
24

Это зависит от того, какие выгоды вы ожидаете получить от неизменности. Ответ Рей Миясаки адресован одному (избегание недопустимых промежуточных состояний), но вот другой.

Мутацию иногда называют деструктивным обновлением : когда вы мутируете объект, старое состояние теряется (если вы не предпримете дополнительные шаги для явного сохранения его каким-либо образом). Напротив, с неизменяемыми данными тривиально представлять состояние одновременно до и после некоторой операции или представлять несколько последующих состояний. Представьте, что вы пытаетесь реализовать поиск в ширину, изменяя один объект состояния.

Это, вероятно, проявляется в мире баз данных чаще всего как временные данные. Скажем, в прошлом месяце вы были на базовом плане, но 16-го вы переключились на премиум-план. Если мы просто переписали какое-то поле, в котором указано, на какой план вы работаете, у нас могут возникнуть трудности с правильным выставлением счетов. Мы также можем упустить возможность анализировать тенденции. (Посмотрите, что сделала эта местная рекламная кампания!)

Это то, что приходит мне в голову, когда вы говорите «неизменность в дизайне базы данных», во всяком случае.

Райан Калпеппер
источник
2
Я не согласен с вашим третьим абзацем. Если вы хотите иметь историю (журнал аудита, журнал изменений плана и т. Д.), Вам нужно создать для этого отдельную таблицу. Дублирование всех 50 полей Customerтаблицы только для того, чтобы помнить, что пользователь изменил план, не приносит ничего, кроме огромного недостатка производительности, более медленного выбора с течением времени, более сложного интеллектуального анализа данных (по сравнению с журналами) и большего тратящегося пространства.
Арсений Мурзенко
6
@MainMa: возможно, я должен был просто сказать «иди почитать о временных базах данных». Мой пример был задуман как набросок временных данных; Я не утверждаю, что это всегда лучший способ представлять изменяющиеся данные. С другой стороны, хотя поддержка временных данных в настоящее время довольно слабая, я ожидаю, что тенденция будет заключаться в размещении временных данных в самой базе данных, а не в переводе их в представления «второго класса», такие как журналы изменений.
Райан Калпеппер
Что если мы сохраним историю изменений в таблице аудита (например, весенняя загрузка и спящий режим отключают эту возможность)?
Мохаммад Наджар
14

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

Datomic - это база данных, изобретенная Rich Hickey в сочетании с Think Relevance. Существует множество видеороликов, в которых рассказывается об архитектуре, целях и модели данных. Поиск infoq, в частности, один называется Datomic, База данных как значение . В разделе «Конфликты» вы можете найти лейтмотив, который Рич Хики дал на конференции euroclojure в 2012 году. Confreaks.com/videos/2077-euroclojure2012-day-2-keynote-the-datomic-architecture-and-data-model

В vimeo.com/53162418 есть разговор, который больше ориентирован на развитие.

Вот еще один из Стюарт Хэллоуэй в .pscdn.net/008/00102/videoplatform/kv/121105techconf_close.html

  • Datomic представляет собой базу данных фактов во времени, называемых датумами, в 5 кортежах [E, A, V, T, O]
    • E Entity ID
    • Имя атрибута в сущности (может иметь пространства имен)
    • V Значение атрибута
    • T Transaction ID, с этим у вас есть представление о времени.
    • O Одна операция утверждения (текущая или текущая стоимость), отклонение (прошедшая стоимость);
  • Использует собственный формат данных, который называется EDN (расширяемая нотация данных)
  • Транзакции КИСЛОТНЫЕ
  • Использует журнал данных в качестве языка запросов, который декларативен как SQL + рекурсивные запросы. Запросы представлены структурами данных и расширены вашим языком jvm, вам не нужно использовать clojure.
  • База данных разделена на 3 отдельных сервиса (процессы, машины):
    • Сделка
    • Место хранения
    • Query Engine.
  • Вы можете отдельно масштабировать каждую услугу.
  • Это не с открытым исходным кодом, но есть бесплатная (как в пиве) версия Datomic.
  • Вы можете указать гибкую схему.
    • набор атрибутов открыт
    • добавлять новые атрибуты в любое время
    • нет жесткости в определении или запросе

Теперь, поскольку информация хранится как факты во времени:

  • все, что вы делаете, это добавляете факты в базу данных, вы никогда не удаляете их (кроме случаев, когда это требуется по закону)
  • Вы можете кэшировать все навсегда. Query Engine, живет на сервере приложений как база данных в памяти (для языков jvm не-jvm языки имеют доступ через API REST.)
  • Вы можете запросить как раз в прошлом.

База данных является значением и параметром для механизма запросов, QE управляет соединением и кэшированием. Поскольку вы можете видеть базу данных как значение и неизменную структуру данных в памяти, вы можете объединить ее с другой структурой данных, созданной из значений «в будущем», и передать ее в QE и запросить будущие значения, не изменяя фактическую базу данных. ,

Существует проект с открытым исходным кодом от Rich Hickey, который называется codeq , вы можете найти его в github Datomic / codeq, который расширяет модель git и хранит ссылки на объекты git в базе данных без данных, а также выполняет запросы к вашему коду. можно увидеть пример того, как использовать Datomic.

Вы можете думать о datomic как об ACID NoSQL, а с помощью датумов вы можете моделировать таблицы или документы, Kv-хранилища или графики.

Kisai
источник
7

Идея избегать обновлений и предпочтения вставок - одна из идей построения хранилища данных в качестве источника событий, идея, которую вы часто найдете в сочетании с CQRS. В модели источника событий обновление отсутствует: агрегат представляется в виде последовательности его «преобразования» (событий), и в результате хранилище доступно только для добавления.
Этот сайт содержит интересные обсуждения CQRS и источников событий, если вам интересно об этом!

Mathias
источник
CQRS и Event Sourcing становятся в центре внимания в эти дни.
Гюльшан
6

Это имеет очень тесную связь с так называемыми «медленно меняющимися измерениями» в мире хранилищ данных и таблицами «Temporal» или «Bi-Temporal» в других доменах.

Базовая конструкция:

  1. Всегда используйте сгенерированный суррогатный ключ в качестве первичного ключа.
  2. Уникальный идентификатор того, что вы описываете, становится «логическим ключом».
  3. Каждая строка должна иметь как минимум временную метку «ValidFrom» и, возможно, временную метку «ValidTo», а еще более предпочтительно - флаг «Последняя версия».
  4. При «создании» логической сущности вы вставляете новую строку с «Valid From» текущей отметки времени. Необязательный ValidTo установлен на «навсегда» (9999-12-31 23:59:59), а «Последняя версия» - на «Истина».
  5. При последующем обновлении логического объекта. Вы хотя бы вставляете новую строку, как указано выше. Вам также может понадобиться настроить ValidTo в предыдущей версии на «now () - 1 секунда», а в последней версии «False».
    1. При логическом удалении (это работает только с временной меткой ValidTo!) Вы устанавливаете флаг ValidTo в текущей строке на «now () -1 секунд».

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

Недостатки в том, что вы храните намного больше данных, и вам нужно поддерживать больше индексов (по крайней мере, для логического ключа + ValidFrom + ValidTo). Индекс логического ключа + последняя версия значительно ускоряет большинство запросов. Это также усложняет ваш SQL!

Стоит ли это делать, если вам действительно не нужно вести историю и у вас есть требование воссоздать состояние ваших сущностей в данный момент времени, зависит от вас.

Джеймс Андерсон
источник
1

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

PSR
источник
0

Отказ от ответственности: я в значительной степени новичок в БД: p

Тем не менее, этот подход спутниковой передачи данных оказывает непосредственное влияние на производительность:

  • Хорошо меньше трафика на основной таблице
  • Хорошие меньшие строки в основной таблице
  • Плохое требование спутниковых данных означает, что необходим другой поиск
  • Плохо больше места занято, если все объекты существуют в обеих таблицах

в зависимости от ваших требований, вы можете приветствовать это или нет, но это, безусловно, стоит рассмотреть.

Матье М.
источник
-1

Я не понимаю, как вашу схему можно назвать «неизменной».

Что происходит при изменении значения, хранящегося в дополнительной таблице? Похоже, вам нужно будет обновить эту таблицу.

Для того чтобы база данных была действительно неизменной, она должна поддерживаться исключительно "INSERTS". Для этого вам понадобится метод определения «текущей» строки. Это почти всегда оказывается ужасно неэффективным. Вам нужно либо скопировать все предыдущие неизмененные значения, либо собрать воедино текущее состояние из нескольких записей при запросе. Для выбора текущей строки обычно требуется какой-то ужасно грязный SQL, например ( where updTime = (SELECT max(updTime) from myTab where id = ?).

Эта проблема часто возникает в DataWarehousing, где вам необходимо вести историю данных с течением времени и иметь возможность выбирать состояние для любого заданного момента времени. Решением обычно являются «размерные» таблицы. Однако, пока они решают проблему DW «кто был торговым представителем в прошлом январе». Они не предоставляют никаких преимуществ, которые имеют неизменные классы Javas.

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

Джеймс Андерсон
источник
Для одной записи, WHERE id = {} ORDER BY updTime DESC LIMIT 1как правило, не слишком неэффективно.
Изката
@Izkata - попробуйте положить в середину соединения за тремя столами :-)
Джеймс Андерсон