數據庫大數據量導出多線程版本

【不積跬步,無以至千里;不積小流,無以成江海。

一、概述
一年多前,我做了一個小需求,導出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出馬了。

四、附上一些源碼
【歡迎留言指出文中的問題,互相交流,共同進步,謝謝!!!您的留言是我進步的最大動力】

發佈了76 篇原創文章 · 獲贊 109 · 訪問量 31萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章