系統分析設計 一個JOIN問題解決方案的感想 重視業務分析設計


    Richie
    2006-12-31
    RicCC:http://www.cnblogs.com/RicCC/archive/2006/12/31/608925.html

    這是某個系統的一個做法,覺得對架構師在系統分析設計思想上有所啟發,所以寫出來跟大家分享。
    一個大系統,業務和系統都非常復雜,系統很靈活,但是后台提交到數據庫的SQL語句,基本都是最簡單的SELECT、DELETE、UPDATE、INSERT,你看不到復雜語句的出現,包括JOIN。而對於我們自己開發的系統,哪怕再簡單不過,不使用JOIN似乎不可能。

    用一個簡單的例子來說明。有一個銷售訂單對象,它會關聯到客戶對象。我們假設客戶對象以一個客戶編碼作為主鍵,值是唯一的,然后會有客戶名稱、地區/城市、地址等資料。銷售訂單表有一個客戶編碼字段,跟客戶對象關聯。用戶需求可能是通過客戶名稱、地區/城市等相關信息查詢銷售訂單。

    這種情況下,有一種設計方案,是在SalesOrder表中建立一個CustomerName字段,銷售訂單創建的時候就把客戶名稱的值帶過去,在查詢上只允許按照客戶名稱對SalesOrder表進行查詢,避免使用SalesOrder表與Customer關聯。這種方案的優缺點,大家會有各自的看法。首先,如果需要添加按照客戶地區/城市來查詢SalesOrder,得在SalesOrder添加客戶地區/城市的字段,如果還有其它的對象跟SalesOrder關聯,可能也得添加字段用於查詢;其次某些情況下,這種用法能夠跟業務需求比較好的吻合起來,但大部分情況下,如果客戶名稱有發生變化,怎么辦?維持SalesOrder表中原有的客戶名稱值不變,還是對所有添加了客戶名稱的表進行同步更新?如果不是因為業務需求確實需要這樣做,這種做法是不大合理的。
    接下來看一些比較常規的做法。直接寫SQL,使用SalesOrder Inner Join Customer進行查詢,或者使用HQL,或者在ORM上配置對象關聯關系等方式實現。
    首先,如果在ORM上配置關聯關系實現,一旦查詢復雜一些,並且系統這種查詢很多,會使系統實現或配置很復雜。所以對於這種情況,ORM都會盡量避免,要么直接使用SQL,或者象Hibenate使用HQL實現。
    直接使用SQL時,一方面造成業務模型對邏輯封裝的泄漏,另一方面是對數據持久化媒體的直接暴露。使用HQL能夠做到一些彌補。
    最后我們從性能和效率方面分析一下。
    不管是ORM配置對象關聯關系來實現,還是使用HQL,最終框架都是生成Join語句進行數據庫查詢。SalesOrder和Customer只是一個最簡單的示例,實際中大家遇到的關聯需求可能極為復雜。因為業務的復雜性,對關聯表中索引的需求也是多樣的,難以保證每個查詢以最高的效率使用索引。比較極端的情況,也很可能常遇到,象上面的例子,可能需要先對SalesOrder和Customer兩個表做全表掃描,然后對這兩個表所有數據進行Join操作。假若SalesOrder和Customer都是幾十萬、幾百萬的數據量(當然這種數據量下肯定不會讓這種事情發生,只是舉例),這個查詢性能會是什么樣子?SQL Server也好Oracle也好,都會非常差的。可能不少的系統都會面臨一些復雜查詢的性能問題,解決起來確實比較棘手。
    在數據量沒有這么多,能夠在一定程度利用索引的時候,情況會好一些,但這也只是限於一定的情況下。各方面的原因,都可能造成我們難以確保所有復雜查詢都充分利用索引,隨着業務數據的變化,索引的使用效率也會發生變化。當單個的查詢對數據庫服務器造成比較大的消耗時,並發數越大,系統的性能下降越厲害,並且帶來一系列互相關聯又互相影響的問題,阻塞嚴重,死鎖概率變大。對稍復雜的系統,出現這種情況時,在系統層面做優化有可能是件無法實施的事情,因此不少的系統會選擇將過期的數據導出。
    另外效率方面是指,如果這個查詢結果比較多,絕大部分情況下對於用戶而言這是一個無效查詢,因為用戶不大可能在大量數據里面再去檢索他想要的數據。面對這種情況,用戶極有可能會憑記憶,查文檔查郵件等其它方式找到一些更精確的條件,縮小查詢結果的范圍。有的人會提出,結果集是按特定字段排序的,用戶拉動滾動條、翻頁等可能會比較方便的找到他想要的數據。對於CS下面的產品,這的確是有可能,在BS上,我們不可能一個頁面顯示太多數據,翻頁去找用戶一般是不大願意的,並且,對於那些被用戶忽略的數據,我們還是白白耗費了服務器資源,是否有更好的方式避免這種情況呢?另外有的人可能會提出,添加一個類似SQL Server里面的Top語句,或者使用Oracle的Rowid限制結果集數量。其實這種做法在上面示例的情況下,除了減少網絡傳輸方面比較明顯,在數據庫查詢方面並不能帶來多少效率的提高,因為數據庫仍然得完成Join等操作之后,才能知道Top或者是Rowid所指定的范圍,你從執行計划里面都可以發現這一點。這種低效的查詢,耗費服務器資源做無用功,當你看着服務器內存、CPU、I/O消耗很高,而執行的查詢有效性卻比較低時,作為架構師、設計師的你,會有什么感想?

    其實,過於復雜SQL的出現,復雜SQL所帶來的系統性能問題,本身就直接意味着系統架構、分析設計、開發方面的問題。
    象上面提到的那個系統,不使用一個JOIN,大家可能會覺得它走了一個極端。它是通過在系統分析、設計上下足功夫來實現的,在下面了解它的做法之后,我們能夠知道,它的做法增加了一些與數據庫的交互,在用戶多的情況下並發情況可能會稍嚴重一些,但跟上面的分析相比較,這絕對是不足以提的。並且這種情況下遇到的數據庫性能問題,我們可以認為是一種正常的良性的狀態,是一種必然,通過提高服務器配置,調整數據庫服務器架構等,能夠比較容易和明顯的改善,而不用調整系統。

    一般的做法下,使用JOIN方式,界面差不多是這個樣子,這也是很直接很自然的一種方法:

    為了避免使用JOIN,界面就只允許按照客戶編碼的字段進行查詢了,界面的樣子如下:

    當用戶需要根據其他客戶資料查詢銷售訂單時,將這個操作拆分成兩個步驟。第一步根據客戶的其他資料先准確查詢到這個客戶,這個步驟通過Customer NO.查詢條件輸入框后面的圖標按鈕,彈出一個詳細的客戶資料查詢界面。當准確查詢到某個客戶資料並確定之后,彈出的客戶查詢界面將客戶編碼返回,填入到Customer NO.查詢條件輸入框中。第二步就是根據返回的客戶編碼查詢銷售訂單。
    當然你可能會想說,這種做法用戶使用起來不方便,因為只有一個客戶編碼的輸入框。作為系統分析設計人員,需要能敏銳地覺察到這種問題,一個訂單查詢的用例,經過這樣分拆之后,可能的確給用戶的操作帶來局限性,所以需要進一步思考完善。看一下下面的界面:

    首先,Customer NO.輸入框有多組,用戶可以輸入多個值進行查詢;在第三行Customer NO.輸入框位置有一個向下的圖標按鈕,點擊之后可以動態添加一組Customer NO.輸入框。其次一般的系統,象這種客戶編碼、銷售訂單編號等,都是有規則的,相同的前綴可能意味着屬於同一個類別,因此每一組Customer NO.條件都使用From、To的查詢方式,就是說那些 From值 =< Customer NO. =< To值。另外,假如你有一個Excel文件,里面有10個客戶編碼的列表,現在你需要查詢這10個客戶某個月份的銷售訂單,一個一個輸入可能還是比較麻煩,這種情況下可以提供一個上傳查詢條件列表文件的方式,避免用戶頻繁輸入。
    說明一下,參考的系統是CS結構,在BS上實現可能有些繁瑣復雜,我們不去探究這些方面,而是把它當作一種分析設計方法參考。
 
    從效率和性能方面分析一下。將原本需要JOIN完成的查詢拆分成了兩個步驟進行,每一步都是進行一個單表查詢。在單表查詢的基礎上做性能方面的考慮,這個問題就變得非常單純,容易很多,例如索引的建立和使用,使用Top、Rowid等方式提高數據庫的使用效率和查詢性能(默認返回500、200條記錄,用戶可以輸入具體的數字到底返回多少條)等。如果你對數據庫的理解比較透徹,用前面提到的SalesOrder和Customer都是幾十萬、幾百萬數據量的情況做一下對比,如果再加上Top、Rowid等方式的使用,用戶完成同樣的一個任務,數據庫的開銷可能是1個甚至幾個數量級的改善。
    在系統開發方面,簡潔、清晰。一個客戶查詢對話框,可以在所有類似使用客戶資料進行查詢的地方使用。數據列表的顯示只是基於單個對象,有利於做成通用的控件、組件方式,比較自然的嵌入在整體技術架構中。如果查詢條件部分也能夠控件化,你的系統界面開發會是一個全新的面貌。
    在架構設計方面的優點,大家可以想到的可能更多。原本一個用例需要結合兩個對象一起考慮,現在每一步驟只是針對一個對象操作,這樣有利於業務對象邏輯的完整封裝。對復雜關聯關系的考慮,造成技術框架方方面面的復雜性,隨着這個拆解消失。例如ORM,如果不需要關聯關系,不需要HQL,根本不需要考慮開發者會自己定義一個查詢,返回一個比較隨意的DataSet而不是Domain Model Object,你對ORM的開發、選擇還會有什么考慮?
    復雜的系統邏輯,並不會因為上面這樣一個簡簡單單的方案就能解決所有的JOIN問題,不同的場景下你需要進行不同的分析,例如有的情況下適當的添加冗余表、冗余字段等。

    啰嗦的寫了一大段,並不是想告訴大家這里有這樣一個解決JOIN的方法,大家以后不要用JOIN了。而只是想結合在這樣一個示例中說明一下業務分析、設計的重要性。很多時候我們太過依賴於技術而忽視業務分析設計,而去追求一組互相矛盾的目標:開發維護高效方便、系統靈活擴展性伸縮性良好等等,因此我們開始進入一個漫漫的旅程,對技術的要求越來越高,對框架的要求越來越多也越復雜,而你卻始終都發現還是難以接近目標。如果你在業務分析設計上多花一些精力,可能取得的效果會完全不一樣。
    最后祝大家新年快樂!


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com