9 17 CSDN 社區(qū)的 Java 技術(shù)欄中看到了網(wǎng)友 lifejoy 的一個(gè)貼子,題為“ hibernate 寫(xiě)入性能暴差,如何配置?”,詳細(xì)鏈接請(qǐng)見(jiàn)

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

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

          ?

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

          <!--[if !supportLists]-->1.???????? <!--[endif]-->Hibernate ORM讀寫(xiě)與JDBC方式讀寫(xiě)在性能上孰優(yōu)孰劣?

          <!--[if !supportLists]-->2.???????? <!--[endif]-->優(yōu)勢(shì)多少?劣勢(shì)又是幾何?

          ?

          依照 lifejoy 的思路下寫(xiě)以下一段代碼:

          ?

          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 創(chuàng)建 10000 條記錄,分 10 次插入,每次 1000 條,每 100 條記錄做一次批量插入

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

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

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

          ?????? }

          ??????

          ??????

          ?????? // hibernate 創(chuàng)建 person 記錄 , loopNum 為循環(huán)插入的次數(shù), batchNum1 為每次循環(huán)插入的記錄數(shù), batchNum2 為物理批量插入記錄數(shù)

          ?????? 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 ) {// 執(zhí)行物理批量插入

          ?????????????????????????????????? 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 創(chuàng)建 person 記錄 , loopNum 為循環(huán)插入的次數(shù), batchNum1 為每次循環(huán)插入的記錄數(shù), batchNum2 為物理批量插入記錄數(shù)

          ?????? 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){// 執(zhí)行物理批量插入

          ????????????????????????????????????????? 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 數(shù)據(jù)庫(kù)環(huán)境

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

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

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

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

          ?????? }

          ??????

          ?????? // 銷(xiāo)毀 hibernate 數(shù)據(jù)庫(kù)環(huán)境

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

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

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

          ?????? }

          }

          ?

          ?

          測(cè)試環(huán)境主要為: J2SDK1.4.2_04, MySql4.1.9-Max, Hibernate3.1, IBM Thinkpad R32-P4 1.8G, 512M Memory ; MySql 中待插表的類(lèi)型為 INNODB ,以支持事務(wù), ISAM 類(lèi)型表的讀寫(xiě)速率要遠(yuǎn)高于 INNODB ,這里不采用 ISAM 是因?yàn)椴恢С质聞?wù)。

          ?

          主要分為三個(gè)測(cè)試場(chǎng)景,以下為三個(gè)場(chǎng)景的測(cè)試記錄和分析:

          ?

          測(cè)試場(chǎng)景一:

          ############# 測(cè)試環(huán)境一 #######################

          mysql 版本: 4.1.9 -max

          jdbc 驅(qū)動(dòng): mysql-connector-java-3.1.11-bin.jar

          hibernate: 3.1

          ?

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

          ?

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

          測(cè)試記錄:

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

          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

          ?

          測(cè)試結(jié)果:

          hibernate 新記錄創(chuàng)建平均速率: ~290 /

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

          ?

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

          測(cè)試記錄:

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

          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

          ?

          測(cè)試結(jié)果:

          jdbc 新記錄創(chuàng)建平均速率: ~1147 /

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

          ?

          ?

          ****** 測(cè)試環(huán)境一結(jié)論: jdbc 性能明顯優(yōu)于 hibernate ,寫(xiě)入速率比 jdbc/hibernate=3.95

          ?

          ?

          ?

          測(cè)試場(chǎng)景二:

          ############# 測(cè)試環(huán)境二 #######################

          mysql 版本: 4.1.9 -max

          jdbc 驅(qū)動(dòng): mysql-connector-java-3.0.11-bin.jar (注意這里更換了 mysql connectorJ 驅(qū)動(dòng)!?。。?/span>

          hibernate: 3.1

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

          ?

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

          測(cè)試記錄:

          ======================================================================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

          ?

          測(cè)試結(jié)果:

          新記錄創(chuàng)建平均速率: ~974 /

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

          ?

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

          測(cè)試記錄:

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

          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

          ?

          測(cè)試結(jié)果:

          新記錄創(chuàng)建平均速率: ~1497 /

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

          ****** 測(cè)試環(huán)境二結(jié)論: jdbc 性能仍優(yōu)于 hibernate ,寫(xiě)入速率比 jdbc/hibernate =1.58

          ?

          ?

          測(cè)試場(chǎng)景三:

          ############# 測(cè)試環(huán)境三 #######################

          mysql 版本: 4.1.9 -max

          jdbc 驅(qū)動(dòng): mysql-connector-java-3.0.11-bin.jar 與測(cè)試環(huán)境二使用同樣的驅(qū)動(dòng)

          hibernate: 3.1

          特別說(shuō)明:記錄插入不使用事務(wù)

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

          ?

          1.jdbc 批量插入,創(chuàng)建 10000 條記錄,分 10 次插入,每次 1000 條,每 100 條記錄做一次批量插入操作,不使用事務(wù)注意這里,不使用事務(wù)?。?/span>)

          測(cè)試記錄:

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

          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

          ?

          測(cè)試結(jié)果:

          新記錄創(chuàng)建平均速率: ~50 /

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

          ?

          測(cè)試結(jié)果分析:

          1. 在同等測(cè)試環(huán)境和條件下, hibernate 優(yōu)于 jdbc 這種說(shuō)法是錯(cuò)誤的,從測(cè)試結(jié)果來(lái)看, jdbc 要優(yōu)于 hibernate ,這從理論上是可以理解的, hibernate 的基礎(chǔ)就是 jdbc ,它不可能優(yōu)于 jdbc

          2. 影響數(shù)據(jù)庫(kù)操作性能的因素很多,主要包括:

          1) 數(shù)據(jù)庫(kù)自身

          mysql 表類(lèi)型,是 ISAM 還是 innodb

          2) 數(shù)據(jù)庫(kù)驅(qū)動(dòng)

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

          3) 操作數(shù)據(jù)庫(kù)的程序本身

          測(cè)試環(huán)境 3 表明,當(dāng) mysql 的表類(lèi)型為 innodb 時(shí),即使是采用 JDBC 直接寫(xiě)的方式,不采用事務(wù)方式插入記錄,寫(xiě)入速率幾乎是“蝸速”( ~50 / 秒),這可以說(shuō)是“殺手級(jí)”的因素了。

          ?

          結(jié)論:

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

          <!--[if !supportLists]-->2.? 對(duì)于要獲取高性能數(shù)據(jù)讀寫(xiě)的系統(tǒng),不推薦使用HibernateORM方式進(jìn)行數(shù)據(jù)讀寫(xiě)。<!--[endif]-->

          <!--[if !supportLists]-->3. 性能的優(yōu)劣除了所采用的技術(shù)決定外,更取決于使用技術(shù)的人,比如在測(cè)試環(huán)境三中,不采用事務(wù)方式寫(xiě)數(shù)據(jù),其速度簡(jiǎn)直不能以“暴差”來(lái)形容,想想這樣一種情況,讓你去開(kāi)一輛法拉利F1賽車(chē),你一定能想象得到你駕駛的速度。:)<!--[endif]-->

          ?

          后記:

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


          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           

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

          Copyright © weibogao

          主站蜘蛛池模板: 西和县| 双辽市| 田东县| 文化| 长寿区| 高邑县| 漳州市| 溆浦县| 葫芦岛市| 邵东县| 哈尔滨市| 墨江| 嘉义县| 襄汾县| 巩留县| 房产| 富蕴县| 罗平县| 尉犁县| 香港 | 鞍山市| 行唐县| 肃北| 白朗县| 中江县| 连平县| 临沭县| 老河口市| 清水河县| 通城县| 苍梧县| 门头沟区| 祥云县| 伊春市| 阿拉善盟| 临高县| 宣化县| 淮北市| 城步| 舞阳县| 砀山县|