【发布时间】:2010-12-23 22:06:33
【问题描述】:
我正在编写一个使用 STM 的 clojure 程序。目前我正在从数据库启动时填充 STM(使用 refs),然后在 dosync 事务成功时异步更新数据库。我不知道我是否以正确的方式执行此操作,或者是否有更好的标准技术来执行此操作。谁能向我解释一下他们是如何在 Clojure 程序中将 STM 的 ACI 属性转换为 ACID 的?
【问题讨论】:
我正在编写一个使用 STM 的 clojure 程序。目前我正在从数据库启动时填充 STM(使用 refs),然后在 dosync 事务成功时异步更新数据库。我不知道我是否以正确的方式执行此操作,或者是否有更好的标准技术来执行此操作。谁能向我解释一下他们是如何在 Clojure 程序中将 STM 的 ACI 属性转换为 ACID 的?
【问题讨论】:
一般来说,将 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: Durable refs with ACID guarantees - 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 库。
【讨论】: