Сохранение целостности отношений с Mnesia - PullRequest
10 голосов
/ 27 декабря 2008

Я недавно погрузился в Erlang и решил использовать Mnesia для выполнения своей работы с базой данных, поскольку он может без проблем хранить любую структуру данных Erlang, легко масштабироваться, использоваться со списками и т. Д.

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

Учитывая, что у меня есть эти вымышленные записи, представляющие мои таблицы:

-record(user, {name, salt, pass_hash, email}).
-record(entry, {title, body, slug}).
-record(user_entry, {user_name, entry_title}).

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

Скажите, что пользователь меняет свое имя или заголовок записи изменяется после редактирования. Как мне убедиться, что мои данные все еще правильно связаны? Обновление каждой таблицы с использованием имени пользователя при его изменении звучит как ужасная идея, независимо от того, как она выглядит.

Как лучше всего внедрить какую-либо систему первичных ключей в Mnesia?

Кроме того, как будет действовать промежуточная таблица, такая как 'user_entry', если первое поле обычно является ключом? В противном случае, что может быть лучше, чтобы представлять отношения многих ко многим в Мнесии?

Ответы [ 2 ]

9 голосов
/ 27 декабря 2008

Я предпочитаю использовать GUID вместо автоматически увеличивающихся целых в качестве искусственных внешних ключей. Модуль Erlang uuid доступен по адресу GitHub, или вы можете использовать {now(), node()}, учитывая, что now/0 doc говорит: «Также гарантируется, что последующие вызовы этого BIF возвращают постоянно увеличивающиеся значения.»

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

Не забывайте, что вам не нужно нормализовать данные в Mnesia даже до первой нормальной формы; в вашем примере я бы рассмотрел следующую структуру:

-record(user, {id, name, salt, pass_hash, email, entries}).
-record(entry, {id, title, body, slug, users}).

, где entries и users - списки идентификаторов. Конечно, это зависит от того, какие запросы вы хотите.

РЕДАКТИРОВАТЬ: исправлено много-ко-многим вместо многих-к-одному.

8 голосов
/ 28 декабря 2008

Mnesia поддерживает последовательности (автоинкрементные целые числа) в форме mnesia:dirty_update_counter(Table, Key, Increment). Для его использования вам понадобится таблица с двумя атрибутами Key и Count. Несмотря на название, dirty_update_counter является атомарным, даже если он не выполняется внутри транзакции.

Ульф Вигер проделал определенную работу по предоставлению типичных функций СУБД поверх mnesia в своем пакете rdbms . Его код обеспечивает ограничения внешнего ключа, параметризованные индексы, ограничения значений полей и так далее. К сожалению, этот код не обновлялся в течение двух лет, и, вероятно, его будет сложно запустить без большого опыта работы с Erlang.

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

Если ваши имена пользователей уникальны, вы можете использовать схему:

-record(user, {name, salt, pass_hash, email}).
-record(entry, {posted, title, body, slug, user_name}).

Где posted - это erlang: now () время загрузки статьи. user_name может понадобиться вторичный индекс, если вам часто нужно получить список всех статей для пользователя. Поскольку эти данные разделены на две таблицы, вам придется применять любые ограничения целостности в коде приложения (например, не принимать записи без действительного имени пользователя).

Каждое значение поля в mnesia может быть любым термином Эрланга, поэтому, если вы не знаете, какой уникальный ключ у какого-либо отдельного поля, вы можете комбинировать некоторые поля, чтобы получить значение, которое всегда будет уникальным - возможно {Имя пользователя, DatePosted, TimePosted}. Mnesia позволяет искать частичные ключи с помощью mnesia:select(Table, MatchSpec). MatchSpecs довольно сложно написать вручную, поэтому помните, что ets:fun2ms/1 может преобразовать функцию psuedo erlang в matchspec для вас.

В этом примере fun2ms генерирует нам matchspec для поиска в таблице записей в блоге -record(entry, {key, title, slug, body})., где ключ {Username, {Year, Month, Day}, {Hour, Minute, Second}} - имя пользователя автора и дата и время публикации статьи. Приведенный ниже пример извлекает заголовки всех сообщений в блоге по TargetUsername в декабре 2008 года.

ets:fun2ms(fun (#entry{key={U, {Y,M,_D}, _Time}, title=T})
             when U=:=TargetUsername, Y=:=2008, M=:=12 ->
               T
           end).
...