在Datomic中查询结果分页

时间:2017-11-19 03:14:21

标签: performance clojure pagination datomic

我有一个我想解决的假设情况,但我无法找到理想的答案。假设您有一个可以从查询返回的庞大数据集,您如何对其进行分页以使对内存的影响最小化? datoms API,迭代数据并逐个过滤? index-range API,但我必须做与在datoms API中相同的事情,迭代项目并逐个过滤?执行一个只返回id的初始查询,并对这些id进行分页,以便在另一个查询中使用它们来检索整个数据集?

在SQL中,您通常可以在查询中定义分页:

SELECT col1, col2, ...
 FROM ...
 WHERE ... 
 ORDER BY -- this is a MUST there must be ORDER BY statement
-- the paging comes here
OFFSET     10 ROWS       -- skip 10 rows
FETCH NEXT 10 ROWS ONLY; -- take 10 rows

2 个答案:

答案 0 :(得分:6)

有很多事情需要考虑。

首先,在撰写本文时,Datomic附带的Datalog实现非常渴望,并且不会溢出到磁盘,这意味着Datalog查询的结果集必须适合内存。

这并不意味着Datalog与大结果不兼容,因为您可以让每个Datalog查询只处理一小部分数据。例如,您可以使用Datalog来计算查询的“逻辑”部分(要返回的实体),以及Entity API或Pull API(懒惰地)计算查询的“内容”部分(返回哪些属性)对于每个实体)。鉴于Entity Id只是一个Java Long(8字节),这可以为您节省两个数量级的内存占用量。使用Entity API的示例:

(defn export-customers 
  [db search-criteria]
  (->> 
    ;; logical part - Datalog-based, eager
    (d/q '[:find [?customer ...] :in % $ ?search-criteria :where
           (customer-matches-criteria ?search-criteria ?customer)]
      (my-rules) db search-criteria)
    ;; content part - Entity API based, lazy
    (map (fn [eid]
           (let [customer (d/entity db eid)]
             (select-keys customer 
               [:customer/id 
                :customer/email
                :customer/firstName
                :customer/lastName
                :customer/subscription-time]))))
    ))

您可以通过热切地将整个结果存储在辅助Blob存储中来补充此方法,然后针对该分页进行轮询。

如果您的查询逻辑不是太复杂,您可以想象根本不使用Datalog,例如通过使用原始索引访问(例如使用Datoms API或Index Range API)。

最后,您应该考虑到Datomic不适合为您的分析查询提供服务。因为使用Datomic进行变更检测是微不足道的,所以很容易将派生数据流式传输到辅助存储,以便更好地计算分析查询(例如ElasticSearch,Google BigQuery,PostgreSQL等)。

答案 1 :(得分:0)

您是否看过此页:http://docs.datomic.com/query.html#memory-usage

似乎所有中间结果都必须符合记忆。我认为这也适用于最终结果。

您可以尝试询问:https://forum.datomic.com/

旁注:当Datomic返回并且 实体 时,它是一个"懒惰地图的形式"明确无法完全显示的内容使其具体化,如

(let [plain-map (into {} entity-map) ]
  (println plain-map))