clojure - 数据查询性能改进

标签 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.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 个子任务(几乎总是,但也有一些异常(exception))包含一个参与者。我使用 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 秒。我在这里做错了什么吗?是否可以加快速度?

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

最佳答案

在开始分析等之前,您可以替换

[:find ?taskIds ...]

通过

[:find (pull ?task-entity [*]) ...]

减少与对等方的往返次数,从而摆脱 task-entities 的 map 语句。在第二步中,将 [*] 替换为您真正想要为每个实体提取的适当键集。

关于clojure - 数据查询性能改进,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43361183/

相关文章:

dictionary - 在我的 Clojure 程序中将 map 更改为 pmap 会导致奇怪的异常 (ClassCastException)

oop - 如何在函数式编程中使用多态性?

clojure - Clojure 中最简单的惰性函数

cassandra - datomic 自托管、分布式存储选项的比较?

database - 如何使用 Datomic 数据库执行原子操作?

python - 使用 pyDatalog 解析依赖关系图

java - 需要帮助翻译 foo += (bar - foo) * qux;到 Clojure

clojure - 通过带有 Datomic 和 GPG key 的 SSH 使用 "lein ring server"

database - 更改数据的数据记录模式

count - 答案集编程 - 如何计算我的查询结果中出现的事实数量?