Datomic 中的数据建模

Posted

技术标签:

【中文标题】Datomic 中的数据建模【英文标题】:Data modeling in Datomic 【发布时间】:2012-05-08 14:57:56 【问题描述】:

我一直在研究Datomic,它看起来非常有趣。但是,虽然似乎有 very good information on how Datomic works technically,但我还没有看到太多关于应该如何考虑数据建模的内容。

在 Datomic 中进行数据建模有哪些最佳实践?有没有关于这个主题的好资源?

【问题讨论】:

将链接更改为指向 datomic 教程。 如何为简单博客建模的例子,请看***.com/a/16755573/610484 【参考方案1】:

告诫讲师

由于 Datomic 是新的,而且我对它的经验有限,因此不应以任何方式将此答案视为最佳实践。将其作为 Datomic 的介绍,适合那些具有关系背景并渴望更高效的数据存储的人。

开始

在 Datomic 中,您将域数据建模为 实体,这些实体拥有 属性。因为对另一个 Entity 的引用可以是 AttributeValue,所以您可以对 之间的 Relationships 建模实体简单。

乍一看,这与在传统关系数据库中建模数据的方式并没有什么不同。在 SQL 中,表行是 Entities 和具有 Values 的表的列名称 Attributes关系由一个表行中的外键 Value 表示,该外键引用另一表行的主键 Value

这种相似性很好,因为您可以在为您的域建模时画出传统的 ER 图。您可以像在 SQL 数据库中一样依赖关系,但不需要弄乱外键,因为这是为您处理的。 Datomic 中的写入是事务性的,您的读取是一致的。因此,您可以以任何合适的粒度将数据分成实体,依靠连接来提供更大的图景。这是您在使用许多 NoSQL 存储时失去的便利,在这些存储中,通常拥有大型非规范化实体以在更新期间实现某种有用的原子性水平。

此时,您有了一个良好的开端。但 Datomic 比 SQL 数据库灵活得多。

利用优势

时间本质上是所有 Datomic 数据的一部分,因此无需将数据历史记录作为数据模型的一部分。这可能是 Datomic 最受关注的方面。

在 Datomic 中,您的架构没有严格定义为 SQL 所需的“矩形”。也就是说,entity1 可以具有满足您的模型所需的任何属性。实体不需要有NULL 或不适用于它的属性的默认值。您可以根据需要为特定的单个实体添加属性。

因此,您可以随着时间的推移更改各个实体的形状,以响应您的领域的变化(或您对领域的理解的变化)。所以呢?这与 MongoDB 和 CouchDB 等文档存储不同。

不同之处在于,使用 Datomic,您可以在所有受影响的实体上以原子方式更改架构。这意味着您可以发出一个事务来更新所有实体的形状,基于任意域逻辑用您的语言编写[2],这些实体将在不影响读者的情况下执行,直到坚定的。在关系或文档存储空间中,我不知道有任何接近这种权力的东西。

您的实体也没有被严格定义为“住在一张桌子上”。您决定什么定义了 Datomic 中实体的“类型”。您可以选择明确并强制模型中的每个实体都有一个 :table 属性,该属性表示它是什么“类型”。或者您的实体只需满足每种类型的属性要求即可符合任意数量的“类型”。

例如,您的模型可能要求:

一个人需要属性:name:ssn:dob 员工需要:name:title:salary 居民需要:name:address 会员需要:id:plan:expiration

这意味着像我这样的实体:

:name "Brian" :ssn 123-45-6789 :dob 1976-09-15 
 :address "400 South State St, Chicago, IL 60605"
 :id 42 :plan "Basic" :expiration 2012-05-01

可以推断为PersonResidentMember但不是Employee

Datomic 查询以Datalog 表示,并且可以合并以您自己的语言表示的规则,引用未存储在 Datomic 中的数据和资源。您可以将 数据库函数 存储为 Datomic 中的一等值。这些类似于 SQL 中的存储过程,但可以作为事务内部的值进行操作,并且也可以用您的语言编写。这两个功能都可以让您以更以领域为中心的方式表达查询和更新。

最后,OO 和关系世界之间的impedance mismatch 一直让我感到沮丧。使用功能性、以数据为中心的语言 (Clojure) 有助于实现这一点,但 Datomic 旨在提供一种持久的数据存储,不需要通过脑力体操从代码到存储进行桥接。

例如,从 Datomic 获取的实体看起来和行为类似于 Clojure(或 Java)映射。它可以传递到更高级别的应用程序,而无需转换为对象实例或通用数据结构。遍历该实体的关系将从 Datomic 懒惰地获取相关实体。但是保证它们与原始查询一致,即使面对并发更新。这些实体看起来就像是嵌套在第一个实体中的普通旧地图。

在我看来,这让数据建模更加自然,也少了很多麻烦。

潜在的陷阱

冲突的属性

上面的示例说明了您的模型中的一个潜在缺陷。如果您稍后确定:id 也是Employee 的属性怎么办?解决方案是将您的属性组织到命名空间中。所以你会同时拥有:member/id:employee/id。提前这样做有助于避免以后发生冲突。

一个属性的定义不能改变(暂时)

一旦您将 Datomic 中的属性定义为特定类型、索引与否、唯一等,您以后将无法更改它。我们在这里用 SQL 用语谈论ALTER TABLE ALTER COLUMN。现在,您可以使用正确的定义创建替换属性并移动现有数据。

这听起来可能很糟糕,但事实并非如此。由于事务是序列化的,您可以提交一个创建新属性、将数据复制到其中、解决冲突并删除旧属性的事务。它将在不受其他事务干扰的情况下运行,并且可以利用您的母语中的特定于域的逻辑来完成它。当您发出ALTER TABLE 时,这本质上就是 RDBMS 在幕后所做的事情,但您可以命名规则。

不要成为“糖果店里的孩子”

灵活的架构并不意味着没有数据模型。我建议一些前期计划以理智的方式对事物进行建模,就像对任何其他数据存储一样。在您必须时利用 Datomic 的灵活性,而不仅仅是因为您可以。

避免存储大量不断变化的数据

对于 BLOB 或不断变化的非常大的数据,Datomic 不是一个很好的数据存储。因为它保留了以前值的历史记录并且没有清除旧版本的方法(还)。这种东西几乎总是更适合像 S3 这样的对象存储。更新:有一种方法可以disable history on a per-attribute basis。更新:现在还有excise data的方法;然而,存储对外部对象的引用而不是对象本身可能仍然是处理 BLOB 的最佳方法。将此策略与使用byte arrays 进行比较。

资源

Datomic mailing list Freenode 上的 IRC 频道 #datomic

注意事项

    我指的是行意义上的实体,而不是更恰当地描述为实体类型的表意义上的实体。 我的理解是目前支持 Java 和 Clojure,但未来可能会支持其他 JVM 语言。

【讨论】:

我正在输入回复,但这要好得多。干得好。 这是一个非常好的概述。谢谢! @bkirkbri “不同之处在于,使用 Datomic,您可以在所有受影响的实体上自动更改架构。这意味着您可以根据任意域逻辑发出事务以更新所有实体的形状,编写用你的语言[2],在提交之前不会影响读者。我不知道在关系或文档存储空间中有任何接近这种权力的东西。”你能举个例子吗,没明白吗? @murtaza52 当然可以。我的意思是你可以发出一个执行 Clojure 或 Java 代码的事务,它可以一致地读取数据库中的任何数据,与外部世界(磁盘、网络)通信,计算新的 datom 并将它们以新的属性提交到数据库原子地。我相信我在同一个事务中创建这些新属性是不正确的——Datomic 不允许在定义它的同一个事务中使用属性。虽然我相信这可能在路线图上。希望对您有所帮助。 我还要提到,在单个事务中发布大量的整个数据库模式更改可能是不可取的。由于事务的一次一个序列化,您将在事务的(可能很长的)持续时间内阻止其他写入。在这种情况下,您可以小批量地重塑实体 - 只需确保您的应用程序代码可以处理任一实体形状。【参考方案2】:

来自 bkirkbri 的一个非常好的回答。我想补充一点:

    如果您存储许多相似但不等于“类型”或架构的实体,请在架构中使用类型关键字,例如

    [:db/add #db/id[:db.part/user] :db/ident :article.type/animal]
    [:db/add #db/id[:db.part/user] :db/ident :article.type/weapon]
    [:db/add #db/id[:db.part/user] :db/ident :article.type/candy]

    :db/id #db/id[:db.part/db] :db/ident :文章/类型 :db/valueType :db.type/ref :db/cardinality :db.cardinality/one :db/doc "文章类型" :db.install/_attribute :db.part/db

当您阅读它们时,从查询中获取实体 ID 并使用 datomic.api/entityeid 并在必要时通过按类型分派的多方法解析它们,因为很难对某些属性中的所有属性进行良好的查询更复杂的架构。

【讨论】:

以上是关于Datomic 中的数据建模的主要内容,如果未能解决你的问题,请参考以下文章

DW数据建模 | 浅谈数据仓库建设中的数据建模方法

万字详解ETL和数仓建模

NoSQL 数据建模

Prisma 中的数据建模与关系

Python数据分析在数学建模中的应用汇总(持续更新中!)

✨[面试进阶]在Hive数据仓库中的建模方式是?为什么选择这种建模方式?(Hive篇)✨