我有一个要在网络应用程序中显示的书名列表。如果这本书“值得注意”,我会做一些特别的事情,例如应用背景颜色或附加表情符号。
我想返回一个类似于这样的向量:
[{:db/id 339, :resource-name "Notation as a Tool of Thought"}
{:db/id 338, :resource-name "The Science of Radio", :notable? :true}
{:db/id 337, :resource-name "Journey Into Mathematics"}
{:db/id 336, :resource-name "Street Fighting Mathematics"}
...]
对 400 个左右的实体范围执行以下具有 3 个 attr-id(包括
pull-many
)的 :db/id
查询需要约 2,900 毫秒:
(require '[datahike.api :as d]) ; version 0.6.1531
(d/pull-many @conn [:db/id :resource-name :notable?]
(range 1 400))
缓慢的查询时间是 EAV 数据库固有的权衡,还是我未能以某种非常明显的方式进行优化?
这个问题最初出现在 Clojurians slack 的 DataHike 频道中,所以我将那里的答案编辑为更长的单个帖子答案。 DataHike 是 Clojure/ClojureScript 中 Datalog 查询引擎的众多实现之一。
您没有提及您正在使用哪个 Clojure 环境。如果您使用 Babashka,它是解释性的,因此非常慢,查询时间可能是合理的。
如果您使用普通的 JVM Clojure(往往性能最高),我希望得到更快的结果。我从 Datomic 获得的经验是,这样的查询应该快很多倍。
DataScript(DataHike 最初基于的数据记录实现)有时可能会有点慢,但对于 clojure/script 环境来说,结果似乎仍然有点太慢。
DataScript中pull-api的实现与DataHike中pull-api的实现有很大不同,其中DataHike似乎做了更多的簿记和检查DataScript(这使得它更慢,但也更容易得到正确)。
JIT/更好的性能指标
您的查询最多包含数据库中的 400 个实体。这可能很少会触发 JIT 重新编译/优化(这使得在长时间运行的生产代码中这一切变得更快)。 Criterium (clojure) 或 Tufte(Criterium 的 ClojureScript 端口)等工具将运行大量测试,以确保 JVM 或 JavaScript VM 是“热”的,并且大多数 JIT 优化已经就位。
我建议您使用 Criterium 或 Tufte 测试性能。
DataHike 仍然更具实验性(但可用),并且具有与 DataScript 不同的范围,这可能会使查询引擎仍然慢得多,但它不应该这么慢。