【问题标题】:Is it suboptimal to add the same datoms multiple times?多次添加相同的数据是否不是最优的?
【发布时间】:2016-12-23 16:47:17
【问题描述】:

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

这是我的问题的简化版本:

  • 我需要解析一个小英文句子列表,并将完整的句子及其单词插入 Datomic。
  • 包含句子列表的文件很大(> 10 GB)
  • 同一个句子可以在文件中出现多次,它们的词也可以跨句子出现多次
  • 在插入过程中,将设置一个属性以将每个句子与其对应的单词相关联

为了简化插入过程,我很想多次编写相同的 datom(即不检查数据库中是否已存在记录)。但我担心性能影响。

  • 如果多次添加相同的 datom,在 Datomic 中会发生什么?
  • 是否值得在事务之前检查是否已经添加了一个 datom?

  • 有没有办法防止 Datomic 覆盖以前的 datom(即如果记录已经存在,则跳过事务)?

感谢您的帮助

【问题讨论】:

  • 您也可以浏览 Datomic Google Group 邮件列表:groups.google.com/forum/#!forum/datomic
  • 实习单词。一个句子是一个单词序列。将其表示为这样。那么句子可能更紧凑。当然,将单词与句子关联起来不需要额外的属性。

标签: clojure datomic datalog


【解决方案1】:
  • 如果多次添加相同的 datom,在 Datomic 中会发生什么?
  • 是否值得检查在事务之前已经添加了一个 datom?

从逻辑上讲,Datomic 数据库是一组有序的 datom,因此多次添加同一个 datom 是幂等的。但是,当您使用 tempid 断言 datom 时,您可以创建一个新 datom 来表示与旧 datom 相同的信息。这就是:db/unique 的用武之地。

为确保实体不会被多次存储,您希望将 :db/unique 属性属性设置为 :db.unique/identity 以获得正确的属性。例如,如果您的架构包含 3 个属性 :word/text:sentence/text:sentence/words,则 :word/text:sentence/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)}]

关于性能:

您可能根本不需要优化,但在我看来,您的导入过程的潜在性能瓶颈是:

  1. 在 Transactor 中构建事务所花费的时间(包括唯一属性的索引查找等)
  2. 构建索引所花费的时间。

改进2.:当你插入的数据排序后,索引速度更快,所以插入单词和句子是排序的。您可以使用 Unix 工具对大文件进行排序,即使它们不适合内存。所以这个过程是:

  • 排序句子,插入它们 (:sentence/text)
  • 提取单词、排序、插入 (:word/text)
  • 插入词句关系 (:sentence/words)

为了改进1.:确实,它可以减轻交易者的压力,将实体ID 用于已经存储的单词而不是整个单词文本(这需要索引查找以确保唯一性)。一个想法可能是在 Peer 上执行该查找,或者通过利用并行性和/或仅针对常用词(例如,您可以插入前 1000 个句子中的词,然后检索它们的实体 ID 并将它们保存在哈希图中)。

就我个人而言,除非经验表明它们是必要的,否则我不会进行这些优化。

【讨论】:

    【解决方案2】:

    您不必担心像这样的预优化。零售计算机商店以大约 0.05 美元/GB 的价格出售硬盘,因此您在这里谈论的是价值 50 美分的存储空间。使用 Datomic 的内置存储压缩,这将更小。索引和其他开销会稍微增加总数,但仍然太小而无需担心。

    与任何问题一样,最好逐步建立解决方案。因此,也许可以用前 1% 的数据做一个实验,并用最简单的算法计算时间。如果这相当快,请尝试 10%。您现在可以很好地估计整个问题加载数据需要多长时间。我敢打赌,查询数据会比加载更快。

    如果您在前 1% 或 10% 之后遇到障碍,那么您可以考虑重新设计设计。由于您已经构建了具体的东西,因此您不得不更详细地考虑问题和解决方案。这比挥手争论和白板设计要好得多。您现在对您的数据和可能的解决方案实施有了更多的了解。

    如果事实证明最简单的解决方案无法在更大范围内发挥作用,那么在拥有第一个解决方案的经验后,第二个解决方案将更容易设计和实施。最终解决方案很少会从您的脑海中完全形成。对于任何重大问题,计划反复细化解决方案非常重要。

    这本开创性书中我最喜欢的章节之一 The Mythical Man Month 由 Fred Brooks 撰写,标题为“Plan To Throw One”。

    【讨论】:

      【解决方案3】:
      • 如果多次添加相同的 datom,Datomic 会发生什么情况?

      如果您要添加具有唯一标识 (:db.unique/identity) 的单词/句子,则 Datomic 将在存储中仅保留一份副本(即单个实体)

      • 是否值得检查在事务之前已经添加了一个 datom?
      • 有没有办法防止 Datomic 覆盖以前的 datom(即如果记录已存在,则跳过事务)?*

      同样,使用 :db.unique/identity,则无需查询实体 id 即可检查其存在。

      更多信息请参考here

      【讨论】:

        猜你喜欢
        • 2013-10-16
        • 1970-01-01
        • 2022-01-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-08-18
        • 1970-01-01
        相关资源
        最近更新 更多