我正在编写一个使用 STM 的 clojure 程序。目前我正在从数据库启动时填充 STM(使用 refs),然后在 dosync 事务成功时异步更新数据库。我不知道我是否以正确的方式执行此操作,或者是否有更好的标准技术来执行此操作。谁能向我解释他们如何在 Clojure 程序中将 STM 的 ACI 属性转换为 ACID?
4 回答
通常,将 ACID 中的“D”添加到任何程序都不是一件容易的事,并且取决于程序的要求。在确定实现之前,需要确定一个重要的规范。
是否有对数据库的多线程/多进程访问?
从问题正文中,您的程序似乎仅在启动时读取并在 STM 更改后写入,其中数据库会滞后 STM 中的值一小段时间。但是,如果其他程序(包括程序的其他实例)访问数据库,那么您将需要使用锁,在事务之前锁定对数据库的访问,并在写入数据库后解锁(作为旁注,请注意,您的数据库可以是任何东西,包括文件系统中的简单文件)。当您有多个读取和写入时,没有办法解决这个问题,因为它们都是涉及数据库的副作用。
如果没有多重访问,那么异步编写就可以了,因为代码可以保证始终按顺序工作,因为您的程序在访问时是单线程的。
如果你只有多个写线程,启动后没有读,只有一个实例,那么你只需要保证正确的写顺序。您可以使用代理来执行此操作,其中代理基本上是对数据库的写入操作队列。您将 dosync 包装在参考事务和代理周围,除了持久性之外,还为您提供持久性。
一般来说,涉及副作用的要求越复杂,确保 ACID 所需的技巧就越多。如果您有其他要求,那么我给出的实现可能必须更改。
编辑:
(def db-agent (agent dummy-value))
(defn db-write [_ data] ;; make this intelligent to handle when db is not up
(try
(write-to-db data)
(catch ... database fails, do a retry or let user know of problem))
_)
;; in the transaction code
(dosync
(alter my-ref ...)
(send-off db-agent db-write @my-ref)) ;; ensure db gets written to
您可能对以下内容感兴趣:
Alyssa Kwan 修改后的 Clojure 核心,为 refs 添加了持久性,请参阅: ANN:具有 ACID 保证的 Durable refs - Phase I,ANN:Durable Clojure - Phase II - Persistent Data Structures,ANN:Durable Clojure - Functions and Closures
Sergey Didenko 的库,它没有提供强大的持久性保证,但非常接近: Simple-Persistence-for-Clojure
其他方法对程序员来说可能不是那么透明。
STM 模型非常适合在系统更改时跟踪对系统的多次访问。它不太适合数据持久性,其中更改需要在访问它们的线程的生命周期之外访问。
将 ACID 中的“D”与 STM 分开考虑通常很好
如果您想要一个具有快速内存访问的数据库,并且不时在幕后持续存在,那么请使用真正的数据存储,而不是尝试构建自己的数据库,这将是一项相当大的工作。
Redis和MongoDB是两个不错的选择,但还有很多其他选择。您可以分别在https://github.com/ragnard/redis-clojure和https://github.com/somnium/congomongo找到用于 Redis 和 Mongo 的Clojure 库。