与 Mnesia 保持关系完整性

Posted

技术标签:

【中文标题】与 Mnesia 保持关系完整性【英文标题】:Preserving relational integrity with Mnesia 【发布时间】:2010-09-28 13:18:38 【问题描述】:

我最近一直在研究 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”这样的中间表会怎么做?否则,在 Mnesia 中表示多对多关系的更好方法是什么?

【问题讨论】:

【参考方案1】:

我更喜欢使用 GUID 而不是自动递增整数作为人工外键。有一个Erlang uuid module 可在 GitHub,或者你可以使用now(), node(),因为now/0 doc 说:“还可以保证对该 BIF 的后续调用返回不断增加的值。”

使用可以改变的东西作为主键在我看来是一个独立于数据库系统的坏主意。

不要忘记你不需要将 Mnesia 中的数据标准化为第一范式;在您的示例中,我会考虑以下结构:

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

其中entriesusers 是ID 列表。当然,这取决于您想要的查询。

编辑:固定为多对多而不是多对一。

【讨论】:

真的是好办法吗?似乎 make_ref/0 在重新启动 shell 后重置了它的计数,并且会有多个相似的值。这是否意味着您可能会在服务器重新启动后获得错误的密钥?将它与 now/0 结合起来可能会有所帮助。 实际上,将 now/0 与 node/0 结合起来可能会更好。 或者,你可以只使用 uuid 模块:github.com/travis/erlang-uuid/tree/master【参考方案2】:

Mnesia 确实支持mnesia:dirty_update_counter(Table, Key, Increment) 形式的序列(自动递增整数)。要使用它,您需要一个具有两个属性 Key 和 Count 的表。尽管有这个名字,但dirty_update_counter 是原子的,即使它不在事务中运行。

Ulf Wiger 在他的rdbms package 中做了一些工作,以在 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 中的每个字段值都可以是任何 erlang 术语,因此,如果您不知道任何特定字段上的唯一键,您通常可以组合一些字段来为您提供一个始终唯一的值 - 也许用户名、发布日期、发布时间。 Mnesia 允许您通过mnesia:select(Table, MatchSpec) 搜索部分键。 MatchSpecs 很难手动编写,所以请记住 ets:fun2ms/1 可以为您将伪 erlang 函数转换为 matchspec。

在此示例中,fun2ms 为我们生成了一个匹配规范,用于搜索博客条目表 -record(entry, key, title, slug, body).,其中键为 Username, Year, Month, Day, Hour, Minute, Second - 作者的用户名以及文章发布的日期和时间。下面的示例检索 2008 年 12 月 TargetUsername 的所有博客文章的标题。

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

【讨论】:

我认为“where”应该是“when”。也感谢您的精彩解释。

以上是关于与 Mnesia 保持关系完整性的主要内容,如果未能解决你的问题,请参考以下文章

使用 Perl,如何在保持参照完整性的同时从单个表加载多个表?

电源完整性设计

如何使用 3 个表在 SQL 查询中获得完整结果,其中 1 个表保持 2 个表的关系?

复制关系表数据

实用数据库开发实践MySQL——数据模型

SQL学习之使用命令方式创建删除与修改关系表