我正在使用Clojure的xodus,并正在评估以惰性方式迭代所有键/值对的可能性,就像Clojure中常见的那样。
我最初的理解是,通过Cursor
的所有数据访问应该发生在只读的Transaction
中,因为每个事务都在自己的数据库快照上操作。
但是,如果您在事务中创建了一个游标,那么在事务结束后,似乎仍然可以继续遍历相同的事务快照。实际上,即使光标关闭了,似乎仍然可以使用它。
我猜这不是一种安全的方法,因为我怀疑在某些时候gc会使快照无效。
我仍然有点困惑,到底一个游标在一个特定的事务中可以使用多长时间,我无法在文档中找到答案。
下面是Clojure中的一个示例,演示了在事务完成并重新分配键之后,游标仍然可以用于检索数据的事实。
Using xodus 1.3.232.
(ns chat-bot.xodus-cursor
(:import [jetbrains.exodus.env Environments StoreConfig TransactionalComputable]
[jetbrains.exodus.bindings IntegerBinding]))
(def store-name "test")
(defn startup []
(Environments/newInstance "cursor-test"))
(defn shutdown [env]
(.close env))
(defn fill [env n base]
(.computeInTransaction
env
(reify TransactionalComputable
(compute [this txn]
(let [store (.openStore env store-name StoreConfig/WITHOUT_DUPLICATES txn)]
(doseq [k (range n)]
(.put store txn (IntegerBinding/intToEntry k) (IntegerBinding/intToEntry (+ base k)))))))))
(defn lazy-cursor [txn cursor has-next]
(lazy-seq
(when has-next
(let [kv [(IntegerBinding/entryToInt (.getKey cursor)) (IntegerBinding/entryToInt (.getValue cursor))]]
(println "realized" kv "txn finished" (.isFinished txn))
(cons kv (lazy-cursor txn cursor (.getNext cursor)))))))
(defn get-seq [env]
(.computeInReadonlyTransaction
env
(reify TransactionalComputable
(compute [this txn]
(let [store (.openStore env store-name StoreConfig/WITHOUT_DUPLICATES txn)]
(with-open [cursor (.openCursor store txn)]
(lazy-cursor txn cursor (.getNext cursor))))))))
(defn do-it []
(let [env (startup)]
(fill env 5 0) ;; put some data into the store
(let [kvs0 (get-seq env)] ;; get the data sequence, not realized yet
(fill env 5 10) ;; override the data
(let [kvs1 (get-seq env)] ;; get the data sequence again
(shutdown env)
[kvs0 kvs1])))) ;; return both original and overridden data sequence
输出将是
(def s (do-it)) ;; sequences are still not realized
s ;; output sequences to realize them
realized [0 0] txn finished true
realized [1 1] txn finished true
realized [2 2] txn finished true
realized [3 3] txn finished true
realized [4 4] txn finished true
realized [0 10] txn finished true
realized [1 11] txn finished true
realized [2 12] txn finished true
realized [3 13] txn finished true
realized [4 14] txn finished true
=> [([0 0] [1 1] [2 2] [3 3] [4 4]) ([0 10] [1 11] [2 12] [3 13] [4 14])]
;; the original and the re-assigned key/value sequence is returned
只要您在一段时间后最终完成(abort)只读事务,就可以使它们保持未完成状态。未完成的事务阻止删除数据库GC移动的旧数据。因此,保持事务未完成的时间取决于您的工作负载:写负载越大,时间越短。例如,如果没有那么多写操作,并且数据库大小在几个小时内增加了1-2-3 -3%,那么您可以在几个小时内保持只读事务,而不会对性能产生任何影响。唯一的缺点是,如果你的应用程序不能优雅地关闭数据库,那么在下次启动时,它将从头开始计算文件利用率,也就是说,它将在后台整个数据库中传输。