Recursion Datomic的递归数据日志查询速度非常慢

Recursion Datomic的递归数据日志查询速度非常慢,recursion,graph,clojure,datomic,datalog,Recursion,Graph,Clojure,Datomic,Datalog,我目前正在评估Datomic存储和查询构成本体的解析符号的用例。数据库中总共有225122个符号(实体)(因此它是一个相当大的本体,但对于数据库来说应该不是什么大问题) 结构相当标准,符号也有 包含它们的父符号(如子符号等) 超符号(它们从中继承的符号) 为了更好地访问这些符号,我们为每个符号提供了一个唯一的名称。这将构成以下Datomic模式: [{:db/ident :ml/name, :db/valueType :db.type/string, :db/cardinality :d

我目前正在评估Datomic存储和查询构成本体的解析符号的用例。数据库中总共有225122个符号(实体)(因此它是一个相当大的本体,但对于数据库来说应该不是什么大问题)

结构相当标准,符号也有

  • 包含它们的父符号(如子符号等)
  • 超符号(它们从中继承的符号)
  • 为了更好地访问这些符号,我们为每个符号提供了一个唯一的
    名称
    。这将构成以下Datomic模式:

    [{:db/ident :ml/name,
      :db/valueType :db.type/string,
      :db/cardinality :db.cardinality/one,
      :db/unique :db.unique/identity}
     {:db/ident :ml/parent,
      :db/valueType :db.type/ref,
      :db/index true,
      :db/cardinality :db.cardinality/one}
     {:db/ident :ml/superclass,
      :db/valueType :db.type/ref,
      :db/index true,
      :db/cardinality :db.cardinality/one}]
    
    现在我有了最基本的递归查询“给我symbol
    p
    中包含的所有符号(可传递的)”。用Datomic术语:

    (def rules
      '[
        [(ubersymbol ?c ?p) (?c :ml/parent ?p)]
        [(ubersymbol ?c ?p) (?c :ml/parent ?c1) (ubersymbol ?c1 ?p) ]
        ])
    (q '[:find ?c ?n :in $ % :where
         (ubersymbol ?c ?d) [?d :ml/name "name of a root symbol"] [?c :ml/name ?n]]
       current-db rules)
    
    查询本身(即中等大小的符号)需要55.5秒,并返回80次点击不是毫秒,而是实秒。这只是我想问的关于数据集的最基本的查询(它是用来从web工具帮助建模者理解本体的结构)

    我正在使用内存数据库运行
    datomic-pro-0.9.5554
    ,并使用对等库(我按照指南中的说明启动了服务器)

    非常感谢您的帮助,为Datomic提供案例

    马库斯

    编辑 正如fricke自己发现的那样,这是一个子句排序的问题,但在查询中,而不是在规则集中。更有效的版本是:

    [:find ?c ?n :in $ % :where
       [?d :ml/name "name of a root symbol"]
       (ubersymbol ?c ?d) 
       [?c :ml/name ?n]]
    
    可以通过以下方式进一步改进上述查询:

    • 使用查询参数而不是在查询体中使用动态参数
    • 使用查找引用按输入实体的
      :ml/name
      解析输入实体
    这将产生:

    (d/q
      '[:find ?c ?n :in % $ ?d :where
        (ubersymbol ?c ?d)
        [?c :ml/name ?n]]
      rules current-db [:ml/name "name of a root symbol"])
    

    我的理论是,您的规则不是以Datalog可以针对这种读取模式进行优化的方式编写的,这可能会导致遍历所有实体。我建议按如下方式重写它们:

    [[(ubersymbol ?c ?p) 
      (?c :ml/parent ?p)]
     [(ubersymbol ?c ?p) 
      ;; we bind a child of the ancestor, instead of a parent of the descendant
      (?c1 :ml/parent ?p)
      (ubersymbol ?c ?c1)]]
    
    这种编写规则集的方法经过优化,可以找到某个节点的后代。您最初编写规则集的方法经过优化,可以找到某个节点的应答器


    在我的机器上使用Datomic 0.9.5385对50000个实体的平衡二叉树进行快速基准测试,结果表明,使用第二种方法确实可以获得所需的性能。

    您可以尝试颠倒第二条规则主体的顺序吗?仍然是5秒(我按照建议首先使用最有选择性的子句)奇怪的是,这个更改现在需要10秒。更奇怪的是,如果我用一个未知的
    :ml/name
    进行查询,返回一个空结果也需要10秒(虽然我认为应该快得多)真的很奇怪……我不确定在没有更多数据分布知识的情况下我还能提供更多的帮助。我现在明白了,我必须将
    :ml/name
    替换为第一个子句,因此查询必须是
    (q'[:find?n:in$%:where[?d:ml/name“12880”](uberscope?c?d)[?c:ml/name?n]](db@conn)规则)
    ,则只需40毫秒。谢谢你的一点提示:对于新查询,它也适用于第一个规则集。我猜当
    (uberscope?c?p)
    是第一个,查询引擎在开始时没有对子句进行重新排序以获得限制性最强的子句。Datomic不会尝试优化查询。要加快处理速度,您可以将限制性最强的子句放在第一位。