Clojure 多次添加相同的数据是否不理想?

Clojure 多次添加相同的数据是否不理想?,clojure,datomic,datalog,Clojure,Datomic,Datalog,我目前正在我的一个项目中使用Datomic,有一个问题困扰着我 以下是我的问题的简化版本: 我需要解析一个英语小句子列表,并将完整的句子及其单词插入Datomic 包含句子列表的文件相当大(>10GB) 同一个句子可以在文件中出现多次,它们的单词也可以跨句子出现多次 在插入过程中,属性将设置为将每个句子与其对应的单词相关联 为了简化插入过程,我尝试多次写入相同的DATOM(即不检查数据库中是否已经存在记录)。但我担心对性能的影响 当多次添加相同的DATOM时,Datomic中会发生什么情况

我目前正在我的一个项目中使用Datomic,有一个问题困扰着我

以下是我的问题的简化版本:

  • 我需要解析一个英语小句子列表,并将完整的句子及其单词插入Datomic
  • 包含句子列表的文件相当大(>10GB)
  • 同一个句子可以在文件中出现多次,它们的单词也可以跨句子出现多次
  • 在插入过程中,属性将设置为将每个句子与其对应的单词相关联
为了简化插入过程,我尝试多次写入相同的DATOM(即不检查数据库中是否已经存在记录)。但我担心对性能的影响

  • 当多次添加相同的DATOM时,Datomic中会发生什么情况
  • 是否值得检查在事务之前是否已经添加了datom

  • 是否有方法防止Datomic覆盖以前的DATOM(即,如果记录已存在,则跳过事务)


感谢您的帮助

您现在不需要担心这样的预优化。零售电脑商店的硬盘售价约为0.05美元/GB,所以这里你说的是50美分的存储空间。有了Datomic的内置存储压缩功能,这将变得更小。索引和其他开销会使总开销增加一点,但仍然太小,不值得担心

与任何问题一样,最好以增量方式构建解决方案。所以,也许用你的前1%的数据做一个实验,然后用最简单的算法计时。如果这相当快,试试10%。现在,您可以很好地估计整个问题加载数据所需的时间。我打赌查询数据会比加载更快

如果在第一个1%或10%之后遇到障碍,则可以考虑重新设计。因为你已经建立了一些具体的东西,你不得不更详细地思考问题和解决方案。这比挥手辩论和白板设计要好得多。现在,您对数据和可能的解决方案实现有了更多的了解

如果最简单的解决方案无法在更大范围内工作,那么第二个解决方案将更易于设计和实现,因为它拥有第一个解决方案的经验。最终的解决方案很少从你的头脑中形成。对任何重大问题来说,计划反复完善解决方案都是非常重要的

这本开创性的书中我最喜欢的章节之一 弗雷德·布鲁克斯(Fred Brooks)的《神秘的人月》(The Mythic Man Month)的标题是“计划扔掉一个”

  • 当多次添加相同的DATOM时,Datomic中会发生什么情况
如果要添加具有唯一标识(:db.unique/identity)的单词/句子,则Datomic将在存储器中仅保留其一个副本(即单个实体)

  • 是否值得检查在事务之前是否已经添加了datom
  • 是否有方法防止Datomic覆盖以前的DATOM(即,如果记录已存在,则跳过事务)*
同样,使用:db.unique/identity,则无需查询实体id来检查其存在性

有关更多信息,请参阅

  • 当多次添加相同的DATOM时,Datomic中会发生什么情况
  • 是否值得检查在事务之前是否已经添加了datom
从逻辑上讲,Datomic数据库是一组已排序的datom,因此多次添加相同的datom是幂等的。但是,当您使用tempid断言一个datom时,您可以创建一个新datom来表示与旧datom相同的信息。这就是我们的优势所在

为确保实体不会多次存储,您需要将正确属性的
:db/unique
属性设置为
:db.unique/identity
。例如,如果您的架构包含3个属性
:word/text
:句子/text
:句子/words
,那么
:word/text
:句子/text
应该是
:db.unique/identity
,这将产生以下架构安装事务:

[{:db/cardinality :db.cardinality/one,
  :db/fulltext true,
  :db/index true,
  :db.install/_attribute :db.part/db,
  :db/id #db/id[:db.part/db -1000777],
  :db/ident :sentence/text,
  :db/valueType :db.type/string,
  :db/unique :db.unique/identity}
 {:db/cardinality :db.cardinality/one,
  :db/fulltext true,
  :db/index true,
  :db.install/_attribute :db.part/db,
  :db/id #db/id[:db.part/db -1000778],
  :db/ident :word/text,
  :db/valueType :db.type/string,
  :db/unique :db.unique/identity}
 {:db/cardinality :db.cardinality/many,
  :db/fulltext true,
  :db/index true,
  :db.install/_attribute :db.part/db,
  :db/id #db/id[:db.part/db -1000779],
  :db/ident :sentence/words,
  :db/valueType :db.type/ref}]
然后,用于插入的事务如下所示:

[{:sentence/text "Hello World!"
  :sentence/words [{:word/text "hello"
                    :db/id (d/tempid :db.part/user)}
                   {:word/text "world"
                    :db/id (d/tempid :db.part/user)}]
  :db/id (d/tempid :db.part/user)}]
关于绩效: 您可能根本不需要优化,但在我看来,导入过程的潜在性能瓶颈是:

  • 在事务处理程序中构建事务所花费的时间(包括唯一属性的索引查找等)
  • 建立索引所花费的时间
  • 改进
    2.
    :对插入的数据进行排序后,索引速度更快,因此最好插入排序后的单词和句子。您可以使用Unix工具对大文件进行排序,即使它们不适合内存。因此,这一过程将是:

    • 对句子进行排序,插入它们(
      :句子/文本
    • 提取单词,对它们进行排序,然后插入它们(
      :word/text
    • 插入单词-句子关系(
      :句子/单词
    改进
    1.
    :事实上,对于已经存储的单词而不是整个单词文本(需要索引查找以确保唯一性)使用实体ID,可以减少事务处理人的压力。一种方法是在对等机上执行该查找,或者利用并行性和/或仅对频繁出现的单词执行查找(例如,您可以插入前1000个句子中的单词,然后检索它们的实体ID并将它们保存在哈希映射中)


    就我个人而言,在经验证明这些优化是必要的之前,我不会进行这些优化。

    您也可以使用来浏览Datomic Google Group邮件列表:Intern the words。一句话就是一句话