2011-01-05 36 views
9

“如果您拥有的唯一工具是锤子,将所有东西都当作钉子一样对待,这很诱人。” - 亚伯拉罕马斯洛Clojure中的数据库函数式编程

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

  • 我倾倒数千行,所以我想这样做增量而不是保持整个XML文件在内存中。

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

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

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

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

处理完每个Person后,任务将从Address,Phone等表中检索适当的行并生成嵌套的XML。

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

我正在以正确的方式解决这个问题吗?我可以轻松地使用Java在OO中做到这一点,但那不会很有趣。

顺便说一句,有没有关于FP模式或架构的好书?我有几本关于Clojure,Scala和F#的好书,但尽管每篇都涵盖了语言,但没有一本关于函数式编程设计的“大图”。

+3

据我所知,没有一个“FP建筑师”的书。但是,如果您首尾相接地阅读“纯功能数据结构”,那么您肯定会对如何在现实世界中应用FP概念有更好的了解。见http://www.amazon.com/Purely-Functional-Structures-Chris-Okasaki/dp/0521663504 – 2011-01-05 17:16:16

+0

@克里斯史密斯:我有一个在我的亚马逊愿望清单。我会检查出来的。 – Ralph 2011-01-05 17:25:51

回答

6

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

哇你的对话者我想提出一个观点来证明:使用单线程程序的

  • 性能。
  • 当您增加线程数量时,程序的性能如何提高。
  • 将程序从单线程转移到多线程是多么容易。

您可能创建一个函数来将单个表转储到XML文件。

(defn table-to-xml [name] ...) 

有了这个,你可以计算出所有或者你的代码,将你的关系数据转换为XML的核心任务。

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

你可能会修改table-to-xml接受额外的参数:

(defn table-to-xml [name thread-count] ...) 

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

如果每个表格创建一个XML文件是可接受的,那么每个表格产生一个线程可能是一个简单的胜利。

(map #(future (table-to-xml %)) (table-names)) 

使用只是一个表格,文件和线程之间的一个一对一的关系:作为一个准则,我希望你的代码不包含任何裁判或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))) 

你可能会考虑使用Java执行人库创建一个线程池。

+0

我正在考虑发布XML('persons')的根元素,然后查询数据库中的所有人行并为每行开始一个单独的'Future'。每个Future都负责查询其他表格并生成嵌套的XML元素(地址,电话等),最后返回完整的“person”片段。我面临的更大问题是如何保持大多数功能“纯粹”。使用更高阶的函数可以让我做FP相当于“反转控制”。 – Ralph 2011-01-05 18:52:34

+0

Gotcha。我会更新我的答案以提供更多建议。 – Psyllo 2011-01-05 20:24:12