Datomic中的数据建模

我一直在研究Datomic,它看起来非常有趣。 尽管似乎有关于Datomic如何在技术上工作的非常好的信息,但我还没有看到如何考虑数据建模。

Datomic中的数据建模有哪些最佳实践? 这方面有没有很好的资源?


注意Lector

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

入门

在Datomic中,您将域数据建模为拥有属性值的实体。 由于对另一个实体的引用可以是属性的值,因此您可以简单地为实体之间的关系建模。

首先看,这与数据在传统关系数据库中建模的方式并不完全相同。 在SQL中,表行是实体和具有值的表的列名称属性。 关系由引用另一个表行的主键值的一个表行中的外键值表示。

这种相似性很好,因为您可以在建模域时勾画出传统的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}
    

    可以推断为一个Person ,一个Resident 一个Member 但不是一个Employee

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

    最后,面向对象和关系世界之间的阻抗不匹配一直让我感到沮丧。 使用功能性,以数据为中心的语言(Clojure)可以帮助解决这个问题,但Datomic希望提供一种持久的数据存储,不需要心理体操从代码到存储。

    作为一个例子,从Datomic获取的实体看起来像Clojure(或Java)映射。 它可以传递到更高级别的应用程序,而不需要翻译成对象实例或通用数据结构。 遍历该实体的关系将会懒惰地从Datomic获取相关实体。 但保证他们将与原始查询保持一致,即使面对并发更新。 这些实体看起来是嵌套在第一个实体内的普通旧地图。

    这使得数据建模更加自然和多,更不用说在我看来是一场斗争。

    潜在的缺陷

  • 相冲突的属性

    上面的例子说明了你的模型中潜在的缺陷。 如果您稍后决定:id也是Employee的属性? 解决方案是将您的属性组织到名称空间中。 所以你可以同时拥有:member/id:employee/id 。 提前做此事有助于避免以后发生冲突。

  • 属性的定义不能改变(还)

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

    这听起来可怕,但事实并非如此。 因为事务是序列化的,所以您可以提交一个创建新属性,将数据复制到它,解决冲突并删除旧属性。 它将在没有其他事务干扰的情况下运行,并且可以利用本地语言中特定于领域的逻辑来完成这件事。 这基本上就是RDBMS在发布ALTER TABLE时在后台执行的操作,但是您将规则命名。

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

    灵活的模式并不意味着没有数据模型。 我建议一些前期规划,以与其他数据存储相同的方式对事物建模。 当你需要时,利用Datomic的灵活性,不仅仅是因为你可以。

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

    对于BLOB或不断变化的非常大的数据,Datomic不是一个好的数据存储。 因为它保留了之前值的历史记录,并且没有清除旧版本的方法(尚未)。 这种东西几乎总是适合像S3这样的对象存储。 更新:有一种方法可以根据每个属性禁用历史记录。

  • 资源

  • Datomic邮件列表
  • Freenode上的IRC频道#datomic
  • 笔记

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

  • 来自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 :article/type :db/valueType :db.type/ref :db/cardinality :db.cardinality/one :db/doc "The type of article" :db.install/_attribute :db.part/db}

  • 当你阅读它们时,从查询中获得实体datomic.api/entity并使用datomic.api/entityeid ,然后通过multimethods根据类型进行分析(如果需要的话),因为很难对一些更复杂的模式中的所有属性进行很好的查询。

    链接地址: http://www.djcxy.com/p/65257.html

    上一篇: Data modeling in Datomic

    下一篇: Foreign Key Constraints in Oracle