posts - 27,  comments - 0,  trackbacks - 0

          有個同學,說是系統中出現性能問題了,說是讓我幫助診斷一下。本來是不想花這時間的,結果耐不住對方的死纏亂打,只要答應幫看看。  故事發生的背景是,在文件上傳的時候,有時間會有人上傳了文件,但是最后沒有使用上傳的文件,這樣就會產生一些垃圾文件。 原來軟件作者就想寫一個后臺定時任務程序,來清除這些垃圾文件? 由于作者堅定的不讓我發她的SQL語句(這個我也理解,這么丑陋的SQL),所以這里就不發源代碼了,發偽代碼。

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          void deleteMissLinkFile{
             List fileList=getFileList();
             List deleteFileList=new ArrayList();
             for(file:fileList){
                 int count1=execute(select count(*) from ...);
                 int count2=execute(select count(*) from ...);
                 int count3=execute(select count(*) from ...);
                 int count4=execute(select count(*) from ...);
                 int count5=execute(select count(*) from ...);
                 if(count1==0&&count2==0&&count3==0&&count4==0&&count5==0){
                     deleteFileList.add(file);
                 }
             }
             delete(deleteFileList);
          }

            

          當然,這里我已經給進行了一定的加工,使得看起一漂亮了許多,實際上,嗯嗯,實在是丑。  這個時候的性能情況是怎么樣的呢?說是表里的數據只有500多條,但是執行時間要100多秒,但是實際上實際的應用場景都遠不止這個數量級,而且隨著數據的增加,性能會呈指數級下降。 我說你去加10萬條記錄測試一下,保證你一晚上算不出來。 好吧,廢話少說,接下來看看怎么優化這段程序。 在開始之前,我們可以假設有N個文件,有M個文件引用表,而且假設所有的文件引用表中的記錄條數都一樣。 很顯然,原來的實現方法中執行了:1次文件數查詢+N*M次統計操作 最笨的優化方法 先用成本最低的方式來優化一把:

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          void deleteMissLinkFile{
             List fileList=getFileList();
             List deleteFileList=new ArrayList();
             for(file:fileList){
                 int count1=execute(select count(*) from ...);
                 int count2=execute(select count(*) from ...);
                 int count3=execute(select count(*) from ...);
                 int count4=execute(select count(*) from ...);
                 int count5=execute(select count(*) from ...);
                 if(count1==0&&count2==0&&count3==0&&count4==0&&count5==0){
                     deleteFileList.add(file);
                 }
             }
             delete(deleteFileList);
          }

            

          嗯嗯,通過上面的重構,性能馬上就可以提升一倍。難看是難看了一點,但是1倍也是不小的提升哦。  原因,原來是要把所有的統計值都算出來,再進行判斷,通過上面的重構,平均只要查一半就可以退出了,所以性能會有1倍的提升。 1次文件數查詢+N*M/2次統計操作 一般的優化方法 偶當時提醒她說,你可以把內外換換,性能就會提升許多,結果死活聽不懂,<ignore_js_op>

          實際上邏輯是這樣的,由于統計操作的執行效率是非常低的,而帶主鍵的查詢速度是非??斓模簿褪前堰壿嫃模罕闅v所有的文件看看引用次數是多少,改變成從所有文件列表中刪除所有已經引用的文件,其余就是要刪除的垃圾文件。

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          void deleteMissLinkFile{
             List fileList=getFileList();
             List refList1=execute(select file from tb1…)
             for(ref:refList1){
                 fileList.remove(ref)
             }
             List refList2=execute(select file from tb2…)
             for(ref:refList2){
                 fileList.remove(ref)
             }
             ……
             delete(deleteFileList);
          }

            

          通過上面的優化,需要執行的SQL語句是:  1+m 條SQL語句,其它都是大量的內存數據比對,相對來說,性能會高太多,通過一定的技巧進行一些優化,會有更大的提升。 這種方式,我毛估估比原始的方式,可以提高兩個數量級以上。 為什么提高了兩個左右數量級還是說比較笨的方法呢? 因為這種方法雖然比原始的方法有了顯著的提升,但是還是存在嚴重的設計問題的。 首先,當數據量比較小的時候(這里的小是指與互聯網應用中的數據相比),做完全遍歷是沒有問題的,但是當數據量比較大的時候,用一條SQL來遍歷所有的數據,就是有非常大的問題的。這個時候就要引入一系列的復雜問題來解決,比如:把單機計算變成集群計算,把整個計算變成分段時間,不管怎么樣,都是非常復雜的處理過程。 無為而治的方法 下面就要推出最快的、最省事的、效率最高的方法。 其實一般來說,只要是算法都是有優化空間和余地的,因此一般來說本人很少把話說滿的。這次本人使用了“最”字,那就是用來表明未來已經沒有優化的空間了,那什么樣的算法才能沒有優化的空間呢?答案就是:啥也不做。 當然了,實際上也不可能啥也不做,問題就在哪里,你不做怎么可能好呢? 實際上就是把任務進行一定的分解。通過把架構進行合理的分析與設計,把所有的文件上傳、刪除都做成公共的方法(或服務),在需要與文件打交道的地方,凡是與文件打交道的時候,做如下處理:

          • 文件上傳:在文件上傳數據中加一條數據,比如:文件相關信息,唯一標識,引用次數為0
          • 文件關聯:當數據與文件關聯的時候,修改引用次數為+1
          • 文件取消關聯:當數據與文件取消關聯的時候(一般來說是刪除或編輯的時候置為空或者換成另外一個的時候),修改引用次數為-1

          自次,當要清理垃圾的時候,就非常簡單的了,只要:    select ... from ... where ref_times=0 然后進行相應的清理工作就好。 這個時候就優化了處理模式,并且把文件引用數據的維護分解到業務工作的過程當中,可以極大幅度的提升清理垃圾的處理效率。當然有的人說了:如果這么做,會使得我的業務處理過程變慢,那怎么辦?其實也沒有關系了,你可以把這個變成異步消息的方式,通知文件引用處理去做這件事情就行了,這樣就不會影響到你的業務處理效率了。 總結 通過上面的分析,我們對文件上傳過程中的垃圾清理過程進行優化,并分析了原來的問題之所在,及后面3種優化方式及其優缺點對比。 當然,實際上許多朋友也會有更好的辦法來解決,歡迎大家參與討論,并批評指正。 如果,你喜歡我的博文,請關注我,以便收到我的最新動態。 如果對我的開源框架感興趣,可以從這里獲取到最新的代碼,也可以訪問Tiny官網獲取更多的消息,或到Tiny社區進行即時交流。

          posted on 2015-08-13 15:30 柏然 閱讀(70) 評論(0)  編輯  收藏

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          <2015年8月>
          2627282930311
          2345678
          9101112131415
          16171819202122
          23242526272829
          303112345

          常用鏈接

          留言簿

          隨筆檔案

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 茌平县| 长垣县| 兴业县| 焉耆| 龙南县| 扬州市| 昂仁县| 卓资县| 舞钢市| 宁南县| 富宁县| 合水县| 怀来县| 南涧| 玉树县| 阿尔山市| 体育| 玛曲县| 清镇市| 莱阳市| 万安县| 久治县| 秭归县| 镇沅| 茶陵县| 广灵县| 子长县| 万盛区| 长治县| 郧西县| 台中市| 莆田市| 南溪县| 肇州县| 渭源县| 仁布县| 澄城县| 白水县| 阿克苏市| 汽车| 青海省|