1. 程式人生 > >資料庫大資料量匯出多執行緒版本

資料庫大資料量匯出多執行緒版本

【不積跬步,無以至千里;不積小流,無以成江海。 一、概述
一年多前,我做了一個小需求,匯出80w的資料。當時寫了一篇部落格簡單地講了一些原理,並貼出了部分的原始碼。原理用了一張圖來表述:

基本就是客戶在頁面申請匯出請求,把請求存在資料庫中,再由定時任務取出來執行:
由於當時是把所有的請求都轉給第一臺機器執行,並且是單執行緒執行<本來是想多執行緒處理,後來因為時間原因,沒有實施。這裡主要是藉助定時鐘機器,所以實現起來比較簡單。定時鐘機器在一臺固定的機器上面執行>,效率低下,因為多個任務需要排隊,業務方的抱怨也很多。為此此次我把定時鐘的單執行緒執行改成了多執行緒執行,且每臺機器都可以執行。下面大致說下原理。
二、多執行緒執行 我們線上的伺服器肯定不止一臺,假設有三臺,每個伺服器最好不止一個執行緒在服務,根據伺服器的能力,假設開3個執行緒。那麼總共就有9個執行緒在服務了。這9個執行緒需要怎麼協調協調等等一些問題就浮出水面了。 一般有兩種結構模式:
第一種:master/slave模式:master到資料庫中拿出所有的任務,再分配給不同的執行緒去執行任務,各個執行緒完成任務後,由master合併任務且標記完成任務。此可以把一個任務給分片,線上程處理完成後,master合併.
第二種:自立模式:就是各個處理器都是對等的,他們都去獲取任務,執行任務,再標記完成任務。各個處理器互不干擾.
分析兩種模式:第一種“master/slave模式”可以對單個任務分片處理。第二種“自立模式”實現起來比較簡單,不需要叢集內部複雜的通訊,關鍵就是怎麼處理多個執行緒、怎麼併發獲取任務。
分析我們的需求發現,我們匯出的任務基本處理時間1s---40m處理時間不等,對於40m的匯出任務是要分片,分片可以很好的解決匯出時間長的問題,但是由於實現起來比較複雜,沒有現成的框架,所以暫時就沒有分片。
以下主要採取的是 多執行緒模式的實現思路:
需要考慮的問題:
1、怎麼併發獲取任務?
2、執行緒輪詢週期性時間為多少?
3、怎麼保證剛生成的任務能立即處理?
【怎麼併發獲取任務呢?】 直接從資料庫中查詢一個任務,再嘗試啟動該任務,如果啟動不了(被別的執行緒捷足先登了),則再重新獲取任務:
【執行緒輪詢週期性時間為多少?】 不可能讓執行緒一直輪詢,這樣會浪費CPU大量的時間,所以採取每隔一段時間休眠的方式.

【怎麼保證剛生成的任務能立即處理?】
由於執行緒輪詢需要時間,為了能保證立即執行,所以程式碼中加了一個LOCK的鎖,當發現沒有任務時,執行緒會在此鎖上等待2分鐘,在2分鐘內,如果LOCK.notifyAll了,那麼執行緒就會立馬喚醒,load任務並執行任務。上圖上程式碼所示。在生成任務時,就會呼叫LOCK.notifyAll。 大致的結構圖如下:叢集內的伺服器不需要互相互動。簡化處理步驟.
整個資料庫匯出分為三個步驟: 第一步:生成任務
黑色線條部分,客戶請求的任務被寫入 DB中。可能從叢集中的任一個伺服器。
第二步:執行任務
褐色線條部分,其中一個伺服器載入到任務,(一般是客戶申請時的機器,但是也不一定),執行任務,並把匯出的結果壓縮生成到storage儲存上面。
第三步:下載任務
客戶可能從任何一臺伺服器下載任務。資料流可以如圖中的紅色線條走向。
可以提供一個頁面供使用者下載。如下圖所示:

從中我們可以看出,叢集內部的機器之間不需要互相通訊。此簡化開發任務。
三、總結 其實不管你是大資料量還是高併發還是xxx,都離不開最基本的同步、併發的一些問題。在工作中還是有很多的技術亮點可以總結成篇。對於資料庫匯出,首先要解決生成的問題,生成可以多個任務同時執行,減少排隊的時間。如果要減少單個任務的時間,則需要任務分片處理,此涉及到任務能不能分片。嘿嘿,如果下次業務方再抱怨,可以弄成分片處理。那就需要master/slave出馬了。 四、附上一些原始碼 【歡迎留言指出文中的問題,互相交流,共同進步,謝謝!!!您的留言是我進步的最大動力】