VoltDB性能測試

轉自:

https://blog.csdn.net/w_j_w2010/article/details/49997361

voltdb3.0和redis單節點的性能比對

https://blog.csdn.net/fbfsber008/article/details/8584298

VoltDB實時投票應用性能測試

https://my.oschina.net/whchsh/blog/105385

 

voter是votedb開源包中的一個性能測試程序,代碼位於源碼包examples/voter/目錄下。該程序模擬短時間內大量用戶發起投票的場景,測試每秒處理的投票請求(三次讀一次寫的事務)的能力。官方發佈的兩個基於該程序的性能測試報告。測試環境部署在12臺Amazon E2雲主機服務上,服務端版本是voltdb 2.2: 
686K TPS with Spring Framework Web App and VoltDB 
695k TPS with Node.js and VoltDB

本文將深入到voter程序內部,分析該測試的流程,以及給出自己的性能測試結果。 (本文代碼部分的格式正在修改,先請將就看文本的)

1、表結構(ddl.sql)

contestants表存儲候選人編號和名字,該表的數據量較小,並且不會改變。

CREATE TABLE contestants 
( 
  contestant_number integer     NOT NULL 
, contestant_name   varchar(50) NOT NULL 
, CONSTRAINT PK_contestants PRIMARY KEY 
  ( 
    contestant_number 
  ) 
);

vote表存儲每一次投票的信息,包括投票電話、州名和所投的候選人編號。同時該表的會按電話號碼做多節點分區。

CREATE TABLE votes 
( 
  phone_number       bigint     NOT NULL 
, state              varchar(2) NOT NULL 
, contestant_number  integer    NOT NULL 
); 
PARTITION TABLE votes ON COLUMN phone_number;

area_code_state表存儲州編號和州名的映射關係。

CREATE TABLE area_code_state 
( 
  area_code smallint   NOT NULL 
, state     varchar(2) NOT NULL 
, CONSTRAINT PK_area_code_state PRIMARY KEY 
  ( 
    area_code 
  ) 
);

根據voter表創建視圖v_votes_by_phone_number,表示每個電話號碼已經投票的次數(應用限制最大投票次數) 
是的,voltdb支持Create View。根據官方文檔的解釋,VoltDB存儲的是物理視圖,即每次插入、更新、刪除數據都會修改物理視圖中所關聯記錄的數據。

CREATE VIEW v_votes_by_phone_number 
( 
  phone_number 
, num_votes 
) 
AS 
   SELECT phone_number 
        , COUNT(*) 
     FROM votes 
GROUP BY phone_number 
;

根據contestants表創建視圖v_votes_by_contestant_number_state,表示每個候選人來自不同州的投票數。

CREATE VIEW v_votes_by_contestant_number_state 
( 
  contestant_number 
, state 
, num_votes 
) 
AS 
   SELECT contestant_number 
        , state 
        , COUNT(*) 
     FROM votes 
GROUP BY contestant_number 
        , state 
;

2、存儲過程

voter中定義了5個存儲過程,其中主要的Vote存儲過程包含4個statement,三次Select和一次Insert 
通過候選人編號,查詢候選人

public final SQLStmt checkContestantStmt = new SQLStmt( 
        "SELECT contestant_number FROM contestants WHERE contestant_number = ?;");

通過電話號碼,查詢該號碼已投的票數

public final SQLStmt checkVoterStmt = new SQLStmt( 
        "SELECT num_votes FROM v_votes_by_phone_number WHERE phone_number = ?;");

通過州編號,查詢州名

public final SQLStmt checkStateStmt = new SQLStmt( 
        "SELECT state FROM area_code_state WHERE area_code = ?;");

將投票的電話號碼、來源州、投票對象記錄到votes表,同時更新兩個視圖

public final SQLStmt insertVoteStmt = new SQLStmt( 
        "INSERT INTO votes (phone_number, state, contestant_number) VALUES (?, ?, ?);");

存儲過程,輸入投票電話、候選人編號,以及每個電話最大投票數(常量)

public long run(long phoneNumber, int contestantNumber, long maxVotesPerPhoneNumber) { 
    voltQueueSQL(checkContestantStmt, EXPECT_ZERO_OR_ONE_ROW, contestantNumber); 
    voltQueueSQL(checkVoterStmt, EXPECT_ZERO_OR_ONE_ROW, phoneNumber); 
     //根據電話號碼計算所在州區號 
    voltQueueSQL(checkStateStmt, EXPECT_ZERO_OR_ONE_ROW, (short)(phoneNumber / 10000000l)); 
    VoltTable validation[] = voltExecuteSQL(); 
     //驗證輸入的候選人編號是否合法 
    if (validation[0].getRowCount() == 0) { 
        return ERR_INVALID_CONTESTANT; 
    } 
     //驗證該電話投票數是否到達上限 
    if ((validation[1].getRowCount() == 1) && 
            (validation[1].asScalarLong() >= maxVotesPerPhoneNumber)) { 
        return ERR_VOTER_OVER_VOTE_LIMIT; 
    } 
     //如果計算的州區號沒有記錄,投票仍然有效,州名記錄爲XX 
    final String state = (validation[2].getRowCount() > 0) ? validation[2].fetchRow(0).getString(0) : "XX"; 
    //完成投票 
    voltQueueSQL(insertVoteStmt, EXPECT_SCALAR_MATCH(1), phoneNumber, state, contestantNumber); 
    voltExecuteSQL(true); 
    return VOTE_SUCCESSFUL; 
}

另外4個存儲過程沒有列入性能統計,不再具體介紹,含義如下:

  • Initialize.java 初始化區號表和候選人表 
  • GetStateHeatmap 獲取按分區得票數最高的候選人 
  • ContestantWinningStates.java 查詢每個候選人早獲勝的N個州 
  • Results.java 獲得最終投票結果

3、單節點性能測試

測試環境是兩臺雙路E5420服務器,服務端版本採用2013年1月剛發佈的VoltDB 3.0開源版:

  • CPU: 2 * E5420 (qual 2.5G)
  • RAM: 8GB
  • LAN: 1000Mbps
  • 1 client,1 server

性能測試的配置參數包括:

  • 服務端處理線程數,分別取1、2(默認值)、4、8、16
  • 客戶端連接模式,分別是sync同步、jdbc同步,async異步三種。

考察的性能指標包括:

  • 每秒執行的事務數:tps
  • 平均請求延時:latency_avg
  • 95%最大請求延時:latency_95
  • 99%最大請求延時:latency_99

以下是三種請求模式和不同服務端線程數情況下的性能曲線圖,其中8線程異步模式下性能達到11.8萬TPS。而兩種同步模式下的性能最高值則是服務端4線程,分別是6.3萬和5.9萬。 
(需要再次提醒,所統計的TPS性能是指每秒執行的事務數,包括三次讀操作,其中兩次是輕量級表的讀,一次是對大表視圖的讀,以及一次帶主鍵併發互斥的寫操作。) 
image

以下兩張圖分別是平均和95%延時統計。可以看出sync和jdbc兩種同步模式下延時一直維持很低(小於5ms)。而async異步模式下,當服務端線程數小於等於8時,也保持低於20ms的水平,但當服務端線程數爲16,平均和95%最大延時都出現了大幅的增加,分別達到了131ms和350ms。

imageimage

爲了進一步分析上面不同線程數下“延時突變”現象,我有進行了進一步的測試——對線程數進行微調。從4到11每個線程數都測試async異步模式的延時,最終得到如下數據。當線程數低於6時,延時隨線程數緩慢上升。而當線程數在7到10,延時則突然下降到小於10ms區間。當線程數增加到11以上後,又發生了延時數據的突變,產生了大幅增長。

image

僅從已測試數據來看,我們推測當服務端配置線程數接近服務器可用內核數時,VoltDB達到最優吞吐率和較理想的延時波動。當然由於本次測試的CPU不支持Intel超線程技術,不清楚這個關於內核數的推斷應該適用於物理內核數、還是邏輯內核數。 
從VoltDB公佈的技術架構來看,以上推斷也是可以成立的。VoltDB的服務端採用CPU級別的Share-noting設計,爲每個線程分配了固定的主鍵區間,事務操作串行化,這樣可以減少數據在CPU緩存和kenel內存區的加載頻率,從而提高處理性能。當所服務端線程數與內核數相匹配,便恰好符合該設計。

最後總結以下,VoltDB的處理能力不一定超出Redis等KV數據庫,但考慮其SQL和事務能力能力,而KV數據庫如果需要應用控制事務性能往往大打折扣。因此,我們可以認爲VoltDB是一個性能很強大的內存關係數據庫,適合使用在需要實時高性能和事務支持的在線遊戲、交易等業務。 
(好吧,它是AGPL的,我知道,但總算有個開源數據庫供我們學習之)


下期預告

  • VoltDB的集羣如何部署?
  • 爲什麼我把voter服務端部署到兩個節點之後,性能反而比單服務器下降了呢?
  • 如何讓VoltDB客戶端支持路由? 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章