【问题标题】:Query result pagination in DatomicDatomic 中的查询结果分页
【发布时间】:2017-11-19 21:08:20
【问题描述】:

我有一个想解决的假设情况,但我找不到理想的答案。假设您有一个可以从查询返回的庞大数据集,您如何对其进行分页以便对内存的影响最小? datoms API,遍历 datoms 并一一过滤? 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

【问题讨论】:

    标签: performance clojure pagination datomic


    【解决方案1】:

    有很多事情需要考虑。

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

    这并不意味着 Datalog 与大结果不兼容,因为您可以让每个 Datalog 查询只处理一小部分数据。例如,您可以使用 Datalog 来计算查询的“逻辑”部分(要返回什么实体),并使用 Entity API 或 Pull API 来(懒惰地)计算查询的“内容”部分(要返回什么属性每个实体)。鉴于实体 ID 只是一个 Java Long(8 个字节),这可以为您节省两个数量级的内存占用之一。使用实体 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 等)

    【讨论】:

      【解决方案2】:

      你看过这个页面吗:http://docs.datomic.com/query.html#memory-usage

      似乎是说所有中间结果都必须放入内存中。我认为这也适用于最终结果。

      您可以尝试通过以下方式询问:https://forum.datomic.com/


      旁注:当 Datomic 返回和 entity 时,它是一种不完全可见的“惰性映射”形式,明确使其具体化,例如

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

      【讨论】:

      • 还有 (datomic.api/touch my-entity) 在 REPL 检查实体。与 into 的区别在于 touch 返回一个(填充的)实体,而不是一个普通的 Clojure 映射。
      猜你喜欢
      • 1970-01-01
      • 2015-05-20
      • 2020-12-31
      • 2021-09-11
      • 2011-03-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多