9 17 CSDN 社區的 Java 技術欄中看到了網友 lifejoy 的一個貼子,題為“ hibernate 寫入性能暴差,如何配置?”,詳細鏈接請見

          http://community.csdn.net/Expert/TopicView3.asp?id=5025307

          lifejoy 網友寫了段測試程序,用 Hibernate 作為持久手段測試了大數據量寫入 MySql 數據庫的性能。程序主要使用了一個循環嵌套,最里層循環為批量插入記錄的代碼,每一批插 1000 條記錄,最外層循環為批次的控制,一共循環 100 批次,這樣總的數據寫入量為 1000x100 共十萬記錄。從 lifejoy 的測試數據看,用 JDBC 直接寫的速率是 600-800 / 秒,而用 Hibernate 寫的速率會從一開始的 300 多條降至幾十條每秒,這個差距非常之大,難怪 lifejoy 使用了“暴差”這一非常使人觸目驚心的語言。

          ?

          Hibernate 的寫入性能到底如何?真的到了“暴差”這樣的地步么?其性能與 JDBC 直寫相比,到底差距多大?這些個問題,通過 google 結果,眾說紛紜,莫衷一是,在臺灣 JavaWorld 論壇上,有網友貼出了 Hibernate JDBC 性能更加優越的測試結果分析圖,也有很多網友在詬病 Hibernate ORM 的同時喪失了性能,到底真相在何方?由于今年做了一個基于 Oracle 的大型系統,需要支撐高并發數據訪問量,在決定系統架構的時候,首席架構師選擇了 iBatis ,而放棄了 Hibernate ,其中一個最大的考慮就是這個性能因素,可惜當初沒有進行技術實際論證,于是有了今天的這個“考”,打算通過實際測試結果來驗證一下 Hibernate 的性能情況,以澄清如下問題:

          <!--[if !supportLists]-->1.???????? <!--[endif]-->Hibernate ORM讀寫與JDBC方式讀寫在性能上孰優孰劣?

          <!--[if !supportLists]-->2.???????? <!--[endif]-->優勢多少?劣勢又是幾何?

          ?

          依照 lifejoy 的思路下寫以下一段代碼:

          ?

          package com.gmail.newmanhuang.learnhibernate;

          import java.util.Iterator;

          import java.util.List;

          import org.hibernate.SessionFactory;

          import org.hibernate.Session;

          import org.hibernate.Transaction;

          import org.hibernate.cfg.Configuration;

          import org.hibernate.Criteria;

          import org.hibernate.criterion.Expression;

          import com.gmail.newmanhuang.learnhibernate.model.Person;

          import java.sql.*;

          ?

          public class LearnHibernateMain {

          ??????

          ?????? private Configuration config;

          ?????? private SessionFactory sessionFactory;

          ?????? private Session session;

          ??????

          ?????? public static void main(String[] args) {

          ????????????? LearnHibernateMain lh=new LearnHibernateMain();

          ????????????? // hibernate 創建 10000 條記錄,分 10 次插入,每次 1000 條,每 100 條記錄做一次批量插入

          ????????????? //lh.createPersons(10, 1000, 100);

          ????????????? // jdbc 直接創建 10000 條記錄,分 10 次插入,每次 1000 條,每 100 條記錄做一次批量插入

          ????????????? lh.createPersonsByJDBC(10, 1000,100);

          ?????? }

          ??????

          ??????

          ?????? // hibernate 創建 person 記錄 , loopNum 為循環插入的次數, batchNum1 為每次循環插入的記錄數, batchNum2 為物理批量插入記錄數

          ?????? private void createPersons(int loopNum,int batchNum1,int batchNum2){

          ????????????? setup();

          ????????????? System.out.println("hibernate record creating testing.\r\n"

          ??????????????????????????? + "loop times:" + loopNum + "\r\nbatch number:" + batchNum1);

          ?????????????

          ????????????? for(int i=0;i<loopNum;i++){

          ???????????????????? try {

          ??????????????????????????? Thread.sleep(50);// 休眠

          ???????????????????? } catch (InterruptedException e) {

          ??????????????????????????? e.printStackTrace();

          ???????????????????? }

          ???????????????????? long fPoint=System.currentTimeMillis();

          ???????????????????? Transaction tx = session.beginTransaction();

          ???????????????????? for(int j=0;j<batchNum1;j++){

          ??????????????????????????? Person person = new Person();

          ??????????????????????????? person.setName("name-" + i +"-"+ j);

          ??????????????????????????? person.setAge(new Integer(25));

          ??????????????????????????? session.save(person);

          ??????????????????????????? //batch flush

          ??????????????????????????? if ( j % batchNum2 == 0 ) {// 執行物理批量插入

          ?????????????????????????????????? session.flush();

          ?????????????????????????????????? session.clear(); ???????????

          ??????????????????????????? }

          ???????????????????? }

          ???????????????????? tx.commit();

          ???????????????????? long tPoint=System.currentTimeMillis();

          ???????????????????? // 打印插入 batchNum1 條記錄的速率 ( / )

          ???????????????????? System.out.println(

          ?????????????????????????????????? "the " + i + " batch" + "(" + batchNum1 +") rcds/s:"

          ?????????????????????????????????? + ((double)batchNum1/(tPoint-fPoint))*1000);

          ????????????? }

          ????????????? teardown();

          ?????? }

          ??????

          ?????? // jdbc 創建 person 記錄 , loopNum 為循環插入的次數, batchNum1 為每次循環插入的記錄數, batchNum2 為物理批量插入記錄數

          ?????? private void createPersonsByJDBC(int loopNum,int batchNum1,int batchNum2){

          ????????????? System.out.println("JDBC record creating testing.\r\n"

          ??????????????????????????? + "loop times:" + loopNum + "\r\nbatch number:" + batchNum1);

          ????????????? Connection conn=getDBConn();

          ????????????? try{

          ???????????????????? PreparedStatement pstmt=conn.prepareStatement("insert into person(name,age) values(?,?)");

          ???????????????????? for(int i=0;i<loopNum;i++){

          ??????????????????????????? try {

          ?????????????????????????????????? Thread.sleep(50);// 休眠

          ??????????????????????????? } catch (InterruptedException e) {

          ?????????????????????????????????? e.printStackTrace();

          ??????????????????????????? }

          ??????????????????????????? long fPoint=System.currentTimeMillis();

          ??????????????????????????? conn.setAutoCommit(false);

          ??????????????????????????? for(int j=0;j<batchNum1;j++){

          ?????????????????????????????????? String name="name-" + i +"-"+ j;

          ?????????????????????????????????? pstmt.setString(1, name);

          ?????????????????????????????????? pstmt.setInt(2, 25);

          ?????????????????????????????????? pstmt.addBatch();

          ?????????????????????????????????? if(j%batchNum2==0){// 執行物理批量插入

          ????????????????????????????????????????? pstmt.executeBatch();

          ????????????????????????????????????????? conn.commit();

          ?????????????????????????????????? }

          ??????????????????????????? }

          ??????????????????????????? pstmt.executeBatch();

          ??????????????????????????? conn.commit();

          ??????????????????????????? conn.setAutoCommit(true);

          ??????????????????????????? long tPoint=System.currentTimeMillis();

          ??????????????????????????? // 打印插入 batchNum1 條記錄的速率 ( / )

          ??????????????????????????? System.out.println(

          ????????????????????????????????????????? "the " + i + " batch" + "(" + batchNum1 +") rcds/s:"

          ????????????????????????????????????????? + ((double)batchNum1/(tPoint-fPoint))*1000);

          ???????????????????? }

          ???????????????????? pstmt.close();

          ????????????? }catch(Exception x){

          ???????????????????? try{

          ??????????????????????????? conn.close();

          ???????????????????? }catch(Exception x1){

          ???????????????????????????

          ???????????????????? }

          ????????????? }

          ?????? }

          ??????

          ?????? // 獲取 JDBC 連接

          ?????? private Connection getDBConn(){

          ????????????? Connection conn=null;

          ????????????? try {

          ???????????????????? Class.forName("org.gjt.mm.mysql.Driver");

          ???????????????????? conn=DriverManager.getConnection("jdbc:mysql://localhost/learnhibernate", "root", "");

          ????????????? } catch (Exception x) {

          ?

          ????????????? }

          ????????????? return conn;

          ?????? }

          ??????

          ?????? // 初始化 hibernate 數據庫環境

          ?????? private void setup(){

          ????????????? config = new Configuration().configure();

          ????????????? sessionFactory = config.buildSessionFactory();

          ????????????? session = sessionFactory.openSession();

          ?????? }

          ??????

          ?????? // 銷毀 hibernate 數據庫環境

          ?????? private void teardown(){

          ????????????? session.close();

          ????????????? sessionFactory.close();????????

          ?????? }

          }

          ?

          ?

          測試環境主要為: J2SDK1.4.2_04, MySql4.1.9-Max, Hibernate3.1, IBM Thinkpad R32-P4 1.8G, 512M Memory MySql 中待插表的類型為 INNODB ,以支持事務, ISAM 類型表的讀寫速率要遠高于 INNODB ,這里不采用 ISAM 是因為不支持事務。

          ?

          主要分為三個測試場景,以下為三個場景的測試記錄和分析:

          ?

          測試場景一:

          ############# 測試環境一 #######################

          mysql 版本: 4.1.9 -max

          jdbc 驅動: mysql-connector-java-3.1.11-bin.jar

          hibernate: 3.1

          ?

          ################################################

          ?

          1.hibernate 批量插入,創建 10000 條記錄,分 10 次插入,每次 1000 條,每 100 條記錄做一次批量插入操作

          測試記錄:

          ======================================================================

          hibernate record creating testing.

          loop times:10

          batch number:1000

          the 0 batch(1000) rcds/s:172.1763085399449

          the 1 batch(1000) rcds/s:214.73051320592657

          the 2 batch(1000) rcds/s:302.6634382566586

          the 3 batch(1000) rcds/s:321.13037893384717

          the 4 batch(1000) rcds/s:318.9792663476874

          the 5 batch(1000) rcds/s:316.05562579013906

          the 6 batch(1000) rcds/s:318.9792663476874

          the 7 batch(1000) rcds/s:317.05770450221945

          the 8 batch(1000) rcds/s:317.9650238473768

          the 9 batch(1000) rcds/s:314.96062992125985

          ?

          測試結果:

          hibernate 新記錄創建平均速率: ~290 /

          ======================================================================

          ?

          2.jdbc 批量插入,創建 10000 條記錄,分 10 次插入,每次 1000 條,每 100 條記錄做一次批量插入操作

          測試記錄:

          ======================================================================

          JDBC record creating testing.

          loop times:10

          batch number:1000

          the 0 batch(1000) rcds/s:812.3476848090983

          the 1 batch(1000) rcds/s:988.1422924901185

          the 2 batch(1000) rcds/s:1233.0456226880394

          the 3 batch(1000) rcds/s:1314.060446780552

          the 4 batch(1000) rcds/s:1201.923076923077

          the 5 batch(1000) rcds/s:1349.527665317139

          the 6 batch(1000) rcds/s:853.9709649871904

          the 7 batch(1000) rcds/s:1218.026796589525

          the 8 batch(1000) rcds/s:1175.0881316098707

          the 9 batch(1000) rcds/s:1331.5579227696405

          ?

          測試結果:

          jdbc 新記錄創建平均速率: ~1147 /

          ======================================================================

          ?

          ?

          ****** 測試環境一結論: jdbc 性能明顯優于 hibernate ,寫入速率比 jdbc/hibernate=3.95

          ?

          ?

          ?

          測試場景二:

          ############# 測試環境二 #######################

          mysql 版本: 4.1.9 -max

          jdbc 驅動: mysql-connector-java-3.0.11-bin.jar (注意這里更換了 mysql connectorJ 驅動!!!)

          hibernate: 3.1

          ################################################

          ?

          1.hibernate 批量插入,創建 10000 條記錄,分 10 次插入,每次 1000 條,每 100 條記錄做一次批量插入操作

          測試記錄:

          ======================================================================hibernate record creating testing.

          loop times:10

          batch number:1000

          the 0 batch(1000) rcds/s:536.7686527106817

          the 1 batch(1000) rcds/s:504.28643469490675

          the 2 batch(1000) rcds/s:1062.6992561105205

          the 3 batch(1000) rcds/s:1122.334455667789

          the 4 batch(1000) rcds/s:1133.7868480725624

          the 5 batch(1000) rcds/s:1122.334455667789

          the 6 batch(1000) rcds/s:1008.0645161290322

          the 7 batch(1000) rcds/s:1085.7763300760043

          the 8 batch(1000) rcds/s:1074.1138560687434

          the 9 batch(1000) rcds/s:1096.4912280701756

          ?

          測試結果:

          新記錄創建平均速率: ~974 /

          ======================================================================

          ?

          2.jdbc 批量插入,創建 10000 條記錄,分 10 次插入,每次 1000 條,每 100 條記錄做一次批量插入操作

          測試記錄:

          ======================================================================

          JDBC record creating testing.

          loop times:10

          batch number:1000

          the 0 batch(1000) rcds/s:1231.527093596059

          the 1 batch(1000) rcds/s:1406.4697609001407

          the 2 batch(1000) rcds/s:2000.0

          the 3 batch(1000) rcds/s:1692.047377326565

          the 4 batch(1000) rcds/s:1386.9625520110958

          the 5 batch(1000) rcds/s:1349.527665317139

          the 6 batch(1000) rcds/s:1074.1138560687434

          the 7 batch(1000) rcds/s:1386.9625520110958

          the 8 batch(1000) rcds/s:1636.6612111292961

          the 9 batch(1000) rcds/s:1814.8820326678765

          ?

          測試結果:

          新記錄創建平均速率: ~1497 /

          ======================================================================

          ****** 測試環境二結論: jdbc 性能仍優于 hibernate ,寫入速率比 jdbc/hibernate =1.58

          ?

          ?

          測試場景三:

          ############# 測試環境三 #######################

          mysql 版本: 4.1.9 -max

          jdbc 驅動: mysql-connector-java-3.0.11-bin.jar 與測試環境二使用同樣的驅動

          hibernate: 3.1

          特別說明:記錄插入不使用事務

          ################################################

          ?

          1.jdbc 批量插入,創建 10000 條記錄,分 10 次插入,每次 1000 條,每 100 條記錄做一次批量插入操作,不使用事務注意這里,不使用事務!!

          測試記錄:

          ===========================================================================================

          JDBC record creating testing.

          loop times:10

          batch number:1000

          the 0 batch(1000) rcds/s:43.11645755184754

          the 1 batch(1000) rcds/s:34.32651379925854

          the 2 batch(1000) rcds/s:40.65701740120345

          the 3 batch(1000) rcds/s:62.44925997626928

          the 4 batch(1000) rcds/s:69.58942240779402

          the 5 batch(1000) rcds/s:42.45743641998896

          the 6 batch(1000) rcds/s:44.420753375977256

          the 7 batch(1000) rcds/s:44.44049417829527

          the 8 batch(1000) rcds/s:56.63797009515179

          the 9 batch(1000) rcds/s:71.73601147776183

          ?

          測試結果:

          新記錄創建平均速率: ~50 /

          ======================================================================

          ?

          測試結果分析:

          1. 在同等測試環境和條件下, hibernate 優于 jdbc 這種說法是錯誤的,從測試結果來看, jdbc 要優于 hibernate ,這從理論上是可以理解的, hibernate 的基礎就是 jdbc ,它不可能優于 jdbc

          2. 影響數據庫操作性能的因素很多,主要包括:

          1) 數據庫自身

          mysql 表類型,是 ISAM 還是 innodb

          2) 數據庫驅動

          從測試數據和結果看, mysql 3.0.11 版本的驅動顯然更適合于 mysql4.1.9 版本的數據庫,而高版本的 3.1.11 用于 hibernate 的插入操作則會喪失近 3.5 倍的執行效率,另外,經過筆者測試,在 3.1.11 版本的驅動中,使用與不使用批次 (batch) 插入操作居然沒有任何區別,這也能解釋一些技術論壇上提到的 hibernate 批處理操作有時候會實效這個令人困惑的問題。

          3) 操作數據庫的程序本身

          測試環境 3 表明,當 mysql 的表類型為 innodb 時,即使是采用 JDBC 直接寫的方式,不采用事務方式插入記錄,寫入速率幾乎是“蝸速”( ~50 / 秒),這可以說是“殺手級”的因素了。

          ?

          結論:

          <!--[if !supportLists]-->1. 筆者估計在大數據量寫入狀況下,Hibernate的性能損失在30%-35%左右<!--[endif]-->

          <!--[if !supportLists]-->2.? 對于要獲取高性能數據讀寫的系統,不推薦使用HibernateORM方式進行數據讀寫。<!--[endif]-->

          <!--[if !supportLists]-->3. 性能的優劣除了所采用的技術決定外,更取決于使用技術的人,比如在測試環境三中,不采用事務方式寫數據,其速度簡直不能以“暴差”來形容,想想這樣一種情況,讓你去開一輛法拉利F1賽車,你一定能想象得到你駕駛的速度。:)<!--[endif]-->

          ?

          后記:

          在進行測試的時候,起初筆者使用的 JDBC 驅動是 J/Conncector 3.1.11 版本,發現 Hibernate 的批量寫設置根本不起作用,是否使用批量寫根本就沒有差別,在一些網站上面也發現有類似的疑問,經過更換為 3.0.x 版本驅動后,批量寫才生效,而且無論是 Hibernate 方式還是 JDBC 方式下,寫記錄的性能明顯提升,表明 3.0.X 的驅動更適合于 MySql4.1 ,為什么高版本的 3.1.11 反而在低版本數據庫上面表現出低效?筆者在安裝 Roller 這個 Apache 孵化器 blog 項目的時候,也對安裝指導中推薦使用 3.0.X 版本來匹配 MySql4.1 數據庫這個問題比較疑惑,可惜 Roller InstallGuid 沒有做具體解釋,感興趣的網友可以到 Roller 網站的 wiki 上去弄清楚這個問題,并把答案做個回復,非常感謝。這個插曲還說明了一個道理——“升級并非總是好事”。


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


          網站導航:
           

          posts - 41, comments - 7, trackbacks - 0, articles - 0

          Copyright © weibogao

          主站蜘蛛池模板: 离岛区| 长治市| 宜都市| 永德县| 台湾省| 台山市| 嘉义市| 禹城市| 白水县| 涡阳县| 丹江口市| 武清区| 陈巴尔虎旗| 墨玉县| 乐亭县| 白玉县| 子洲县| 荥经县| 曲麻莱县| 读书| 改则县| 蓝田县| 普宁市| 永州市| 青岛市| 二连浩特市| 包头市| 罗甸县| 洛阳市| 会宁县| 巴塘县| 临西县| 清新县| 延长县| 牡丹江市| 武汉市| 垣曲县| 准格尔旗| 德庆县| 祁东县| 边坝县|