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

时间:2016-12-23 16:47:18

标签: clojure datomic datalog

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

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

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

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

  • 多次添加相同数据时,Datomic会发生什么?
  • 是否值得检查在交易之前是否已添加数据?

  • 有没有办法阻止Datomic覆盖以前的数据(即如果记录已存在,请跳过该事务)?

感谢您的帮助

3 个答案:

答案 0 :(得分:2)

  
      
  • 多次添加相同数据时,Datomic会发生什么?
  •   
  • 是否值得检查在交易之前是否已添加数据?
  •   

逻辑上,Datomic数据库是一组有序的数据,因此多次添加相同的数据是幂等的。但是,当您使用tempid声明数据时,可以创建一个新数据来表示与旧数据相同的信息。这是: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. 构建索引所花费的时间。
  3. 改进2.:当您插入的数据被排序时,索引更快,因此可以插入单词和句子。您可以使用Unix工具对大文件进行排序,即使它们不适合内存。所以过程将是:

    • 对句子进行排序,插入(:sentence/text
    • 提取单词,对它们进行排序,插入它们(:word/text
    • 插入字句关系(:sentence/words

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

    就个人而言,在经验表明它们是必要的之前,我不会经历这些优化。

答案 1 :(得分:1)

您不需要担心像这样的预优化。零售电脑商店以大约0.05美元/ GB的价格出售硬盘,所以你在这里谈论的是50美分的存储空间。使用Datomic的内置存储压缩,这将更小。指数&其他开销会增加总量,但它仍然太小而无法担心。

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

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

如果事实证明最简单的解决方案无法在更大范围内发挥作用,那么第二种解决方案将更容易设计和安装。具有第一解决方案经验的工具。最终的解决方案很少会从你的脑海中完全形成。计划重复改进解决方案对任何重大问题都非常重要。

这本开创性书籍中我最喜欢的章节之一 弗雷德布鲁克斯的 神话人月 的标题是“计划扔掉一个”。

答案 2 :(得分:1)

  
      
  • 多次添加相同数据时,Datomic会发生什么?
  •   

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

  
      
  • 是否值得检查在交易之前是否已添加数据?
  •   
  • 有没有办法阻止Datomic覆盖以前的数据(即如果已存在记录,请跳过该事务)?*
  •   

再次使用:db.unique / identity,然后您不需要查询实体ID来检查它的存在。

有关详细信息,请参阅here