Clojure Datomic查询性能改进

Clojure Datomic查询性能改进,clojure,datomic,datalog,Clojure,Datomic,Datalog,我在Datomic数据库中有一个类似的模式: ; --- tenant {:db/id #db/id[:db.part/db] :db/ident :tenant/guid :db/unique :db.unique/identity :db/valueType :db.type/string :db/cardinality :db.cardinality/one :db.

我在Datomic数据库中有一个类似的模式:

; --- tenant
{:db/id                 #db/id[:db.part/db]
 :db/ident              :tenant/guid
 :db/unique             :db.unique/identity
 :db/valueType          :db.type/string
 :db/cardinality        :db.cardinality/one
 :db.install/_attribute :db.part/db}
{:db/id                 #db/id[:db.part/db]
 :db/ident              :tenant/name
 :db/valueType          :db.type/string
 :db/cardinality        :db.cardinality/one
 :db.install/_attribute :db.part/db}
{:db/id                 #db/id[:db.part/db]
 :db/ident              :tenant/taks
 :db/valueType          :db.type/ref
 :db/cardinality        :db.cardinality/many
 :db.install/_attribute :db.part/db}

; --- task
{:db/id                 #db/id[:db.part/db]
 :db/ident              :task/guid
 :db/unique             :db.unique/identity
 :db/valueType          :db.type/string
 :db/cardinality        :db.cardinality/one
 :db.install/_attribute :db.part/db}
{:db/id                 #db/id[:db.part/db]
 :db/ident              :task/createdAt
 :db/valueType          :db.type/instant
 :db/cardinality        :db.cardinality/one
 :db.install/_attribute :db.part/db}
{:db/id                 #db/id[:db.part/db]
 :db/ident              :task/name
 :db/valueType          :db.type/string
 :db/cardinality        :db.cardinality/one
 :db.install/_attribute :db.part/db}
{:db/id                 #db/id[:db.part/db]
 :db/ident              :task/subtasks
 :db/valueType          :db.type/ref
 :db/cardinality        :db.cardinality/many
 :db.install/_attribute :db.part/db}

; --- subtask
{:db/id                 #db/id[:db.part/db]
 :db/ident              :subtask/guid
 :db/valueType          :db.type/string
 :db/cardinality        :db.cardinality/one
 :db/unique             :db.unique/identity
 :db.install/_attribute :db.part/db}
{:db/id                 #db/id[:db.part/db]
 :db/ident              :subtask/type
 :db/valueType          :db.type/string
 :db/cardinality        :db.cardinality/one
 :db.install/_attribute :db.part/db}
{:db/id                 #db/id[:db.part/db]
 :db/ident              :subtask/startedAt
 :db/valueType          :db.type/instant
 :db/cardinality        :db.cardinality/one
 :db.install/_attribute :db.part/db}
{:db/id                 #db/id[:db.part/db]
 :db/ident              :subtask/completedAt
 :db/valueType          :db.type/instant
 :db/cardinality        :db.cardinality/one
 :db.install/_attribute :db.part/db}
{:db/id                 #db/id[:db.part/db]
 :db/ident              :subtask/participants
 :db/valueType          :db.type/ref
 :db/cardinality        :db.cardinality/many
 :db.install/_attribute :db.part/db}

 ; --- participant
{:db/id                 #db/id[:db.part/db]
 :db/ident              :participant/guid
 :db/valueType          :db.type/string
 :db/cardinality        :db.cardinality/one
 :db/unique             :db.unique/identity
 :db.install/_attribute :db.part/db}
{:db/id                 #db/id[:db.part/db]
 :db/ident              :participant/name
 :db/valueType          :db.type/string
 :db/cardinality        :db.cardinality/one
 :db.install/_attribute :db.part/db}     
随着时间的推移,任务是相当静态的,但每个任务平均每5分钟添加和删除一次子任务。我想说的是,每个任务在任何给定的时间平均有大约40个子任务,其中包含(几乎总是,但也有少数例外)一个参与者。我使用Datomic的唯一目的是能够看到任务是如何随时间演变的,也就是说,我希望看到任务在给定时间的样子。为了实现这一目标,我目前正在做类似的事情:

(defn find-tasks-by-tenant-at-time 
    [conn tenant-guid ^long time-epoch]
    (let [db-conn (-> conn d/db (d/as-of (Date. time-epoch)))
          task-ids (->> (d/q '[:find ?taskIds
                              :in $ ?tenantGuid
                              :where
                              [?tenantId :tenant/guid ?tenantGuid]
                              [?tenantId :tenant/tasks ?taskIds]]
                            db-conn tenant-guid)
                       vec flatten)
          task-entities (map #(d/entity db-conn %) task-ids)
          dtos (map (fn [task]
                (letfn [(participant-dto [participant]
                          {:id   (:participant/guid participant)
                           :name (:participant/name participant)})
                        (subtask-dto [subtask]
                          {:id           (:subtask/guid subtask)
                           :type         (:subtask/type subtask)
                           :participants (map participant-dto (:subtask/participants subtask))})]
                  {:id       (:task/guid task)
                   :name     (:task/name task)
                   :subtasks (map subtask-dto (:task/subtasks task))})) task-entities)]
          dtos))
不幸的是,这是非常缓慢的。如果一个租户有许多任务(比如20个),每个任务包含大约40个子任务,那么从这个函数返回大约需要60秒。我是不是做错了什么?有可能加快速度吗

更新:
整个数据集大约为2GB,对等方有3.5Gb的内存(但如果我将其减少到1.5Gb,则似乎没有任何区别),而事务处理方有1GB的内存。我使用的是Datomic Free。

在开始评测之前,您可以替换

[:find ?taskIds ...]


减少到对等方的往返次数,从而消除
任务实体的map语句。在第二步中,将
[*]
替换为您真正想要为每个实体提取的一组适当的键

如果您已经分析了代码以找到瓶颈,那么帮助您会更容易。你可以使用Tufte库来实现这一点。使用pull API而不是实体(你可以在之后转换返回的结果)可能会获得更好的结果@ValentinWaeselynck谢谢你的提示,我会尝试一下,然后再回来。我试过这个,我的印象是它会加快一点,但平均速度仍在20秒左右。基本上,我想拉所有的钥匙,所以我认为更换[*]不会有多大关系。还有什么我可以试试的吗?对不起,我不知道;应正确设置索引,因为:租户/guid是唯一的。我注意到的唯一一件事是模式中的以下输入错误::db/ident:tenant/taksI我现在也尝试提取特定属性,但似乎没有任何区别。你认为值得尝试Datomic Pro并使用SQL作为后端而不是免费使用Datomic吗?所以我猜这不是架构——我认为Pro版本没有任何区别;我还尝试了另一种方法,直接手动读取EAVT和AEVT索引,但也没有加快速度。
[:find (pull ?task-entity [*]) ...]