Datomic中的数据build模

我一直在研究Datomic ,看起来非常有趣。 但是,尽pipeDatomic在技术上的工作方式似乎有很好的信息 ,但我还没有看到如何考虑数据build模。

Datomic中的数据build模有哪些最佳实践? 这个问题有什么好的资源吗?

警告讲师

由于Datomic是新的,我的经验是有限的,这个答案不应被认为是最好的做法。 把它作为Datomic的介绍给那些有关系背景和渴望更高效的数据存储的人。

入门

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

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

这种相似性很好,因为您可以在对域进行build模时勾画出传统的ER图。 您可以像在SQL数据库中那样依赖关系,但是不需要为外键而烦恼,因为这是为您处理的。 在Datomic中写入是事务性的,您的读取是一致的。 因此,您可以将数据以任何粒度感觉正确的方式分解成实体,依靠连接提供更大的图像。 对于许多NoSQL存储来说,这是一个很方便的地方,在更新过程中,通常会有大量的非规范化实体来达到一些有用的primefaces级别。

在这一点上,你是一个好开始。 但是Datomic比SQL数据库更加灵活。

利用

时间本质上是所有Datomic数据的一部分,因此不需要将数据的历史logging包含在数据模型中。 这可能是Datomic最受关注的一个方面。

在Datomic中,您的模式不是在SQL所需的“矩形”中严格定义的。 也就是说,一个实体1可以拥有任何它需要的属性来满足你的模型。 对于不适用的实体,实体不需要具有NULL或默认值。 如果您认为合适,您可以将属性添加到特定的单个实体。

所以你可以随着时间的推移改变个体实体的形状来响应你的域的变化(或者你对域的理解的变化)。 所以呢? 这与文件存储(如MongoDB和CouchDB)不同。

不同的是,使用Datomic,您可以在所有受影响的实体上自动执行模式更改。 这意味着你可以发出一个事务来更新所有实体的形状, 根据 你的语言编写的 任意域逻辑 [2],它将在不影响读者的情况下执行,直到提交。 在关系或文档存储空间中,我没有意识到任何与这种function相近的东西。

您的实体也不是严格定义为“生活在一张桌子上”。 您决定什么定义了Datomic中实体的“types”。 你可以select明确的,并要求你的模型中的每个实体都有一个:table属性,它暗示着“types”是什么。 或者您的实体只需满足每种types的属性要求即可符合任意数量的“types”。

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

  • 一个人需要属性: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

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

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

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

这使得数据build模更加自然和多,更不用说在我看来的战斗。

潜在的缺陷

  • 相冲突的属性

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

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

    一旦你在你的Datomic中定义了一个特定types的属性,索引与否,唯一等,你不能在以后改变它。 我们在这里用SQL语句说ALTER TABLE ALTER COLUMN 。 现在,您可以使用正确的定义创buildreplace属性,并移动现有的数据。

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

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

    灵活的模式并不意味着没有数据模型。 我build议一些前期规划,以一种理智的方式对事物进行build模,就像对其他数据存储一样。 当您需要时 ,利用Datomic的灵活性,不仅仅是因为您可以。

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

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

资源

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

笔记

  1. 我的意思是实体在行的意义上,而不是表义,更恰当地描述为实体types。
  2. 我的理解是Java和Clojure目前得到支持,但将来可能会支持其他JVM语言。

从bkirkbri非常好的答案。 我想做一些补充:

  1. 如果您存储了许多类似但不相同的“types”或模式的实体,请在模式中使用types关键字,如

     [: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:文章/types
    :db / valueType:db.type / ref
    :db / cardinality:db.cardinality / one
    :db / doc“文章的types”
    :db.install / _attribute:db.part / db}

当你阅读它们的时候,从查询中获取实体id,并使用datomic.api/entityeid ,然后通过multimethods分析它们,如果需要的话,在types上调度它,因为很难对一些更复杂的模式中的所有属性进行查询。