1. 程式人生 > 其它 >Fourinone如何實現平行計算和資料庫引擎

Fourinone如何實現平行計算和資料庫引擎

彭淵,在Java技術領域從業十多年,曾撰寫多款開源軟體,歷任淘寶高階專家和華為中介軟體首席架構師。開源代表作有Fourinone(四不像)分散式核心技術框架、CoolHash並行資料庫引擎等,曾出版書籍《大規模分散式系統架構與設計實戰》。

以下為作者分享的整理:

前言:“如何用70行java程式碼實現深度神經網路演算法”一文發表後,反響非常好,為此非常感謝CSDN架構編輯錢曙光先生和機器學習編輯周建丁先生對中國原創技術實踐的支援,並接受邀請,就各位朋友感興趣的分散式核心技術Fourinone(四不像)和高效能並行資料庫引擎CoolHash,在架構師金牌授課群裡做了一次技術分享交流,現將分享內容整理成一篇技術文章釋出出來。題目就叫:Fourinone如何實現平行計算和資料庫引擎。

關於平行計算的概念有非常多,硬體落地其實就只有兩種,CPU上的平行計算和GPU上的平行計算,GPU做點積這樣的向量計算(矩陣計算)有優勢,但目前還執行不了作業系統和資料庫,比較多用於研究性質的計算。在我們生產系統中運用最多的是CPU上的平行計算,其落地方式也只有兩種,多執行緒和多程序。圍繞多執行緒、多程序結合通訊技術的靈活設計,它的應用範圍非常廣泛,不光用於平行計算,也是大部分的伺服器技術、分散式技術、中介軟體技術的重要實現技術,

Fourinone同時提供多執行緒和多程序的平行計算,並且能實現兩者之間的無縫切換,而不需要改一行業務計算的邏輯程式碼,計算過程中還能保證高容錯,一個工人故障會重投任務到其他工人執行,並有超時終止等支援。

一、Fourinone如何實現多執行緒平行計算

實現多執行緒平行計算只需要一個工頭實現類就夠了,除外不需要依賴任何計算服務,這個例子位於下載包的分散式計算補充demo目錄下,CtorMul .java:

import com.fourinone.Contractor;
import com.fourinone.WareHouse;
import com.fourinone.WorkerLocal;


public class CtorMul extends Contractor
{
    public WareHouse giveTask(WareHouse inhouse)
    {
        /*WorkerLocal[] wks = getWaitingWorkers("WorkerMul");
        System.out.println("wks.length:"+wks.length);*/


        //生成5個多執行緒工人,並設定業務實現類
        WorkerLocal[] wks = getLocalWorkers(5);
        for(int j=0;j<wks.length;j++)
            wks[j].setWorker(new WorkerMul());




        //生成15個計算任務
        WareHouse[] tasks = new WareHouse[15];
        for(int i=0;i<15;i++){
            tasks[i]=new WareHouse("taskId",i+"");
        }


        //讓多個工人並行爭搶處理多個任務,並且高容錯,堵塞直到返回所有任務結果。
        WareHouse[] result = doTaskCompete(wks, tasks);
        for(int i=0;i<result.length;i++){
            System.out.println(i+":"+result[i]);
        }


        return inhouse;
    }


    public static void main(String[] args)
    {
        CtorMul cd = new CtorMul();
        cd.giveTask(null);
        cd.exit();
    }
}

直接用java編譯執行CtorMul.java即可完成一個本地多執行緒平行計算:

javac -cp fourinone.jar; *.java
java -cp fourinone.jar; CtorMul

我們看到CtorMul.java程式main函式裡先new了一個CtorMul例項,然後呼叫它的giveTask方法,在這個方法裡,工頭自己生成一定數量的執行緒幫助自己完成計算任務,並在計算結束後程序退出。完成計算任務的函式是doTaskCompete,它有兩個引數,把所有工人和所有任務扔給它,然後堵塞等待所有計算完成,返回一個結果集。

多執行緒平行計算方式的優缺點:

由於只需要一個工頭實現類就可以完成多執行緒平行計算,所以它非常好整合,容易嵌入到你的業務系統中去,但是多執行緒平行計算有很大侷限性,它首先只能在本地,無法做分散式平行計算去利用多臺機器的cpu資源,還有多執行緒的容錯性較差,由於多執行緒工人和主執行緒工頭都在一個jvm程序裡,一個執行緒出故障容易導致整個jvm程序掛掉,也比較難切換到其他執行緒執行。

二、Fourinone如何實現多程序平行計算

如何將上面的多執行緒平行計算無縫切換成一個多機的分散式多程序平行計算,我們把CtorMul.java裡面引用到的WorkerMul.java工人實現類開啟:

import com.fourinone.MigrantWorker;
import com.fourinone.WareHouse;


public class WorkerMul extends MigrantWorker
{
    public WareHouse doTask(WareHouse inhouse)
    {
        String taskId =  inhouse.getString("taskId");
        System.out.print("taskId"+taskId+"任務處理中...");
        try{Thread.sleep(3000L);}catch(Exception ex){}
        System.out.println("taskId"+taskId+"處理完成。");


        return new WareHouse("result", "ok");
    }


    public static void main(String[] args)
    {
        WorkerMul wd = new WorkerMul();
        wd.waitWorking(args[0],Integer.parseInt(args[1]),"WorkerMul");
    }
}

這個工人實現類的doTask接口裡,只是簡單列印了任務編號和sleep了3秒模擬處理任務,然後返回。但是我們注意到它有個main函式的,上面的多執行緒平行計算只是new了WorkerMul 的例項作為業務實現類傳入,但是並沒有執行工人的main函式讓它啟動起來,我們可以讓WorkerMul 獨立啟動,它就是一個工人服務程序,可以在多臺機器上啟動多個這樣的工人程序,並監聽在不同的ip和埠。

我們再觀察到前面的CtorMul .java第一段的程式碼註釋掉了,根據Fourinone的架構設計,我們知道工人服務程序啟動後,會到職介者(ParkServer)去註冊,ParkServer實現了ZooKeeper的所有功能和領導者選舉演算法,然後工頭通過getWaitingWorkers獲取到線上工人,並遠端呼叫工人完成計算任務,詳細可以參考:多機平行計算指南。

我們把CtorMul .java第一段的程式碼的註釋取消,改把第二段用於本地多執行緒的程式碼註釋掉,其他程式碼不用變。然後我們啟動兩個工人做多程序平行計算,重新編譯後執行順序如下:

java -cp fourinone.jar; ParkServerDemo
java -cp fourinone.jar; WorkerMul localhost 6001
java -cp fourinone.jar; WorkerMul localhost 6002
java -cp fourinone.jar; CtorMul

可以看到我們把平行計算從多執行緒切換到了多程序,但是工人的任務實現邏輯doTask仍然一行程式碼不改動。多程序方式需要獨立執行多個工人和職介者服務,比多執行緒方式要麻煩和複雜,但是它能帶來更強大的分散式計算擴充能力和更好的容錯穩定性,我們在執行過程中,可以Ctrl+C把其中一個正在計算的工人關掉,會發現工頭丟擲呼叫異常,但是計算並未中止,而是將該任務重投到另一個工人上去做,只要叢集還剩一個工人,計算就不會受影響,只是計算效率會降低,時間會延長。

總結:有人問Fourinone為什麼不設計成Hadoop,Spark,Storm這樣的動態任務投放方式,實際上Fourinone現在的方式要更靈活,如果要事先定義好DAG那樣的任務流程圖出來,並考慮如何分配資源,做到最後會發現都走到資源隔離管理的路上去了,那還不如開始去做Docker。從這點上看Fourinone的平行計算,更接近MPI,但是相對於MPI抽象歸納出了平行計算的角色、方法和模式。並且Fourinone也沒有Hadoop和Spark的shuffle機制的煩惱,如果Fourinone做成一個資源隔離框架+DAG任務平臺,那是不可能實現出一個功能強大的並行資料庫引擎的,連做些靈活的機器學習演算法並行化都困難。

三、再談並行資料庫引擎CoolHash

關於CoolHash,最好大家能直接它啟動起來,然後各種測,與其談太多架構演算法,不如邊測試邊觀察資料,通過資料去思考。下面是coolhash執行介面,不需要安裝,啟動很簡單。

執行機器可以是普通的筆記本或者桌上型電腦,作業系統不限,CoolHash能竭盡利用你機器的最大效能,當然生產線上還是要執行在pc server上,從8核的虛擬機器到24核的實體機都是可以的,不需要ssd,有更好。相信大家對相同機器配置下,關係型資料庫、記憶體資料庫的基本效能都有所瞭解,可以對比下效能的差異。

每個資料庫廠商出示的測試報告都是宣稱自己最好,所以不要去相信宣傳的,比如作者親身經歷的couchdb單機能力就沒有redis快,客戶方把couchdb公司的人請來最後也是一樣的結果。使用者親手測試的結果更有說服力,看到底能不能在單機上做到百萬的TPS,達到硬體的極限,使用者一定要做到自己心裡有數,如果你對測試結果有什麼疑問,可以直接到Fourinone技術群裡去提問。

想強調的一點,CoolHash是持久化的,幾乎同時把資料從記憶體刷到硬碟,所以它的容量是硬碟容量,不是記憶體容量,這點和redis很不一樣,也就是資料容量超過記憶體大小時,還是執行穩定的,而且效能不下降(準確的說是不高於所在硬碟分割槽容量的70%-80%,作業系統建議不要超過這個安全比例),redis是嚴禁資料容量接近記憶體一半的,因為redis碰到刷硬碟的瞬間佔用記憶體會膨脹一倍。

有人問,我一次寫入200萬資料,為什麼CoolHash裡只有100萬,是不是丟了資料?那是因為CoolHashMap預設最大容量為1百萬條資料,可根據記憶體大小調整HASHCAPACITY配置項,在配置檔案裡改下就可以了。資料庫引擎是嚴禁有任何資料丟失錯亂問題的。

網上有人詆譭CoolHash不支援高併發,最好的辦法就是親自檢驗,不用理會噴子。下載包裡自帶了併發測試程式:

CoolHashTestRun.java是多個客戶端程序高併發大資料量讀寫測試

ThreadClient.java是多個客戶端執行緒高併發大資料量讀寫測試

在CoolHash的最初版本只支援多程序的客戶端,出於安全隔離性考慮限制了一個jvm裡模擬多執行緒客戶端訪問,但是這跟資料庫服務端引擎沒有關係的, 引擎一直都是支援高併發訪問的。現在多執行緒客戶端也都開通了。

CoolHash更重要的能力還不僅是高效能讀寫,靈活檢索能力是衡量資料庫引擎能力的標誌,它能支援key和value的同時模糊檢索,並且可支援高併發檢索,而且都是毫秒級完成,資料量比較大時才幾秒。為了提升檢索能力,可以設計好你的key,用點做分隔符設計一個樹型key,比如user.10010.name,然後用user.*.name來檢索。

傳統關係化結構資料如何轉換設計成CoolHash的樹型key/value結構,可以參考下面這個圖:

除外還有key指標的設計去解決join關聯的問題,CoolHash的很多特性都是其他k/v nosql資料庫沒有的,都來源於作者長期工作實踐中的經驗總結,一個追求創新的資料庫引擎總是會面臨有人對它的各種質疑,技術上有爭論才好,被各大公司都測過,吵過,質疑過,嘗試過,反思過才是對一個開源軟體最好的鞭策和發展。

回到生產場景上,如果僅僅是從技術上驗證一個東西,其實是簡單的,可以先測試,再上poc專案,再上小型生產系統,可以先只做資料同步,再分擔讀寫檢索壓力,執行穩定再上中型生產系統,這樣一步步從小到大應用下去就很容易通過時間去檢驗一項技術。實際上,CoolHash在線上跑了幾個月也從未出過問題,每天10-20G的資料寫入(覆蓋),增量2-3G,整體容量超過幾百g,超出記憶體大小執行,一直很穩定。

CoolHash的並行處理到底是多執行緒模式還是多程序模式?是多程序模式,上面是CoolHash的並行架構示意圖,每個資料工人都是一個獨立程序,多個jvm程序共同作業,這是因為對底層儲存結構、記憶體、mmap等控制上,多執行緒操作容易出問題的,多程序更安全可靠。CoolHash是在單機上採用多程序並行模式,只有單機引擎能力上去了,整體分散式叢集的能力才會提升。如果你需要基於CoolHash做分散式資料庫,可以參考自帶例子AsynClient.java提供的非同步處理等功能支援。

(責編/錢曙光,關注架構和演算法領域)