Clojure中的数据库函数式编程

Ral*_*lph 9 database functional-programming clojure

"如果你拥有的唯一工具是锤子,那就很诱人,把所有东西看作是钉子." - 亚伯拉罕·马斯洛

我需要编写一个工具来将大型分层(SQL)数据库转储到XML.该层次结构由一个的Person与子公司表Address,Phone等表.

  • 我必须转储数千行,所以我想逐步这样做,而不是将整个XML文件保存在内存中.

  • 我想将非纯函数代码隔离到应用程序的一小部分.

  • 我认为这可能是在Clojure中探索FP和并发性的好机会.我还可以向持怀疑态度的同事展示不可变数据和多核利用的好处.

我不确定应用程序的整体架构应该如何.我想我可以使用一个不纯的函数来检索数据库行并返回一个惰性序列,然后可以由返回XML片段的纯函数处理.

对于每一Person行,我可以创建一个Future并且有几个并行处理(输出顺序无关紧要).

当每个Person被处理时,任务将从检索适当的行Address,Phone等等表和生成嵌套XML.

我可以使用通用函数来处理大多数表,依靠数据库元数据来获取列信息,并为需要自定义处理的少数表提供特殊功能.这些功能可以列在一个map(table name -> function).

我是否以正确的方式解决这个问题?我可以轻松地使用Java在OO中执行此操作,但这并不好玩.

顺便说一句,有没有关于FP模式或架构的好书?我有几本关于Clojure,Scala和F#的好书,虽然每个都很好地涵盖了语言,但没有人看过功能编程设计的"大图".

Psy*_*llo 6

好吧,很酷,你用这个作为展示Clojure的机会.所以,你想要演示FP和并发性.收到.

为了让你的对话者惊叹,我想说明一下:

  • 使用单个线程执行程序的性能.
  • 随着线程数量的增加,程序的性能如何提高.
  • 将程序从单线程变为多线程是多么容易.

您可以创建一个函数将单个表转储到XML文件.

(defn table-to-xml [name] ...)
Run Code Online (Sandbox Code Playgroud)

有了它,您可以计算出将关系数据转换为XML的核心任务的全部或代码.

现在你已经解决了核心问题,看看是否会增加更多的线程速度.

您可以修改table-to-xml以接受其他参数:

(defn table-to-xml [name thread-count] ...)
Run Code Online (Sandbox Code Playgroud)

这意味着你有一个线程在一个表上工作.在这种情况下,每个线程可能会处理每第n行.将多个线程放在一个表上的问题是每个线程都想要写入同一个XML文件.这个瓶颈可能会让这个策略变得毫无用处,但值得一试.

如果每个表创建一个XML文件是可以接受的,那么每个表生成一个线程可能很容易获胜.

(map #(future (table-to-xml %)) (table-names))
Run Code Online (Sandbox Code Playgroud)

在表,文件和线程之间只使用一对一的关系:作为指导,我希望你的代码不包含任何refs或dosyncs,解决方案应该非常简单.

一旦开始为每个表生成多个线程,就会增加复杂性,并且可能看不到性能的大幅提升.

在任何情况下,每个表可能会有一个或两个查询来获取值和元数据.关于不想在内存中加载所有数据的注释:每个线程一次只能处理一行.

希望有所帮助!

鉴于您的评论,这里有一些可能有用的伪代码:

(defn write-to-xml [person]
  (dosync
   (with-out-append-writer *path*
     (print-person-as-xml))))

(defn resolve-relation [person table-name one-or-many]
  (let [result (query table-name (:id person))]
    (assoc person table-name (if (= :many one-or-many)
                               result
                               (first result)))))

(defn person-to-xml [person]
  (write-to-xml
   (-> person
       (resolve-relation "phones" :many)
       (resolve-relation "addresses" :many))))

(defn get-people []
  (map convert-to-map (query-db ...)))

(defn people-to-xml []
  (map (fn [person]
         (future (person-to-xml %)))
       (get-people)))
Run Code Online (Sandbox Code Playgroud)

您可以考虑使用Java executors库来创建线程池.