论坛首页 Java企业应用论坛

Hibernate 与 原生 Jdbc 批量插入时的差距

浏览 20285 次
精华帖 (0) :: 良好帖 (4) :: 新手帖 (0) :: 隐藏帖 (1)
作者 正文
   发表时间:2011-05-05  
这帮人是不是闲得蛋疼?老是拿这种没有可比性的东西说七说八的,有意思么?
0 请登录后投票
   发表时间:2011-05-05  
railway 写道
你的表格能说明下什么意思吗?看不太明白,那个J/30、J/50、State/30、State/50、H/30、H/50代表什么?数据库连接?


打个比方, jdbc 连, 一种是使用 addbatch 的方法, 将每一条数据添加至 batch 里面来, 当达到一个数据量的时候, 一次提交至数据库. 这里的 30 / 50 就是这意思. 而 statement 的意思则是说拼 SQL 语句, 如 insert into XXX(id, version) values ('', ''),('', '').... 这样可以将多条数据拼接在一起, 执行的时候会一次插入至数据库, 应该说这种方式是效率最快的. Hibernate 30 50 意思一样.

具体可以看看代码
0 请登录后投票
   发表时间:2011-05-05  
snow0613 写道
这帮人是不是闲得蛋疼?老是拿这种没有可比性的东西说七说八的,有意思么?


我说过, 如果效率在一个可以接受的地步, 我想我会选择 Hibernate 而非 jdbc, 只是这样...
0 请登录后投票
   发表时间:2011-05-05   最后修改:2011-05-05
再说,每种技术都有其适用场景,就跟拿Java和.NET啥的比一样,有意义么?另外,批量操作数据的应用不是没有,但在常用的开发中我想应该不多吧?而且这种数量级的操作,你用JDBC一样也会有性能问题,如果是关系型数据库,建议用存储过程;当然更好的选择是用nosql中比较成熟的产品。这取决于你们的架构师。
记住,别老拿性能说事,看着烦~
0 请登录后投票
   发表时间:2011-05-05  
snow0613 写道
再说,每种技术都有其适用场景,就跟拿Java和.NET啥的比一样,有意义么?另外,批量操作数据的应用不是没有,但在常用的开发中我想应该不多吧?而且这种数量级的操作,你用JDBC一样也会有性能问题,如果是关系型数据库,建议用存储过程;当然更好的选择是用nosql中比较成熟的产品。这取决于你们的架构师。
记住,别老拿性能说事,看着烦~


引用
点快了, 本想发到博客, 却发到论坛了. 主要是坛子里喷的人太多, 只添乱却很少提啥建设性的意见...


呵呵, 总会有那么一些人想要喷几句...
0 请登录后投票
   发表时间:2011-05-05  
snow0613 写道
再说,每种技术都有其适用场景,就跟拿Java和.NET啥的比一样,有意义么?另外,批量操作数据的应用不是没有,但在常用的开发中我想应该不多吧?而且这种数量级的操作,你用JDBC一样也会有性能问题,如果是关系型数据库,建议用存储过程;当然更好的选择是用nosql中比较成熟的产品。这取决于你们的架构师。
记住,别老拿性能说事,看着烦~


"当然更好的选择是用nosql中比较成熟的产品."
你用过吗?
0 请登录后投票
   发表时间:2011-05-05  
在你的架构下,建议使用hibernate
0 请登录后投票
   发表时间:2011-05-06  
如果上到百万、千万、亿级别的数量时,性能会很明显,原生jdbc比hibernate好,用hibernate要一小时的数据量,用原生jdbc十分钟就可以了。
另外,当这么大(百万级别以上)的数据量时,Oracle要比MS Server好很多。
mysql就更不用说了。
0 请登录后投票
   发表时间:2011-05-07  
liu.anxin 写道
railway 写道
你的表格能说明下什么意思吗?看不太明白,那个J/30、J/50、State/30、State/50、H/30、H/50代表什么?数据库连接?


打个比方, jdbc 连, 一种是使用 addbatch 的方法, 将每一条数据添加至 batch 里面来, 当达到一个数据量的时候, 一次提交至数据库. 这里的 30 / 50 就是这意思. 而 statement 的意思则是说拼 SQL 语句, 如 insert into XXX(id, version) values ('', ''),('', '').... 这样可以将多条数据拼接在一起, 执行的时候会一次插入至数据库, 应该说这种方式是效率最快的. Hibernate 30 50 意思一样.

具体可以看看代码


就我的测试来看,PreparedStatement 和 拼装的SQL性能差距巨大,性能差别可以达到100倍.
测试环境:
服务器:E7300,3G,win2003 R2 SP2,Oracle11R2
客户端:一般100M网络与服务器连接,jdbc驱动ojdbc6

测试结果
测试:数据长度[100],每10提交一次
第一步清理耗时:140ms
第一次一般提交耗时:130ms
第二步清理耗时:0ms
第一次Prepared提交耗时:70ms
第二步清理耗时:0ms
第二次Prepared提交耗时:20ms
第三步清理耗时:0ms
第二次一般提交耗时:100ms
-----------------------------------------------
测试:数据长度[1000],每100提交一次
第一步清理耗时:0ms
第一次一般提交耗时:1140ms
第二步清理耗时:10ms
第一次Prepared提交耗时:40ms
第二步清理耗时:10ms
第二次Prepared提交耗时:40ms
第三步清理耗时:10ms
第二次一般提交耗时:850ms
-----------------------------------------------
测试:数据长度[10000],每1000提交一次
第一步清理耗时:20ms
第一次一般提交耗时:10540ms
第二步清理耗时:80ms
第一次Prepared提交耗时:80ms
第二步清理耗时:80ms
第二次Prepared提交耗时:60ms
第三步清理耗时:80ms
第二次一般提交耗时:10530ms
-----------------------------------------------
测试:数据长度[100000],每1000提交一次
第一步清理耗时:80ms
第一次一般提交耗时:109780ms
第二步清理耗时:750ms
第一次Prepared提交耗时:660ms
第二步清理耗时:830ms
第二次Prepared提交耗时:600ms
第三步清理耗时:1970ms
第二次一般提交耗时:109800ms
-----------------------------------------------


public class test {

    public static Connection cn;

    static {
        try {
            Class.forName("oracle.jdbc.OracleDriver");
            cn = DriverManager.getConnection//隐去
        } catch (SQLException ex) {
            Logger.getLogger(test.class.getName()).log(Level.SEVERE, null, ex);
        } catch (ClassNotFoundException ex) {
            Logger.getLogger(test.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public static void main(String[] args) throws SQLException {
        cn.setAutoCommit(false);
        test(100, 10);
        test(1000, 100);
        test(10000, 1000);
        test(100000, 1000);
        cn.close();
    }

    static void test(int count, int commitlength) throws SQLException {
        System.out.println("测试:数据长度[" + count + "],每" + commitlength + "提交一次");
        Long d1 = System.currentTimeMillis();
        clear();
        Long d2 = System.currentTimeMillis();
        updateString(count, commitlength);
        Long d3 = System.currentTimeMillis();
        clear();
        Long d4 = System.currentTimeMillis();
        updatePrepared(count, commitlength);
        Long d5 = System.currentTimeMillis();
        clear();
        Long d6 = System.currentTimeMillis();
        updatePrepared(count, commitlength);
        Long d7 = System.currentTimeMillis();
        clear();
        Long d8 = System.currentTimeMillis();
        updateString(count, commitlength);
        Long d9 = System.currentTimeMillis();

        System.out.println("第一步清理耗时:" + (d2 - d1) + "ms");
        System.out.println("第一次一般提交耗时:" + (d3 - d2) + "ms");
        System.out.println("第二步清理耗时:" + (d4 - d3) + "ms");
        System.out.println("第一次Prepared提交耗时:" + (d5 - d4) + "ms");
        System.out.println("第二步清理耗时:" + (d6 - d5) + "ms");
        System.out.println("第二次Prepared提交耗时:" + (d7 - d6) + "ms");
        System.out.println("第三步清理耗时:" + (d8 - d7) + "ms");
        System.out.println("第二次一般提交耗时:" + (d9 - d8) + "ms");
        System.out.println("-----------------------------------------------");
    }

    static void updateString(int count, int commitlength) throws SQLException {
        String sqlhead = "insert into ZH(zh,product,state) values";
        Statement stmt = cn.createStatement();
        int k = 0;
        for (int i = 0; i < count; i++) {
            k++;
            String zh = "zh" + i;
            String pro = "pro" + i;
            String state = "state" + i;
            StringBuilder sb = new StringBuilder();
            sb.append(sqlhead).append(" ('").append(zh).append("','").append(pro).append("','").append(state).append("')");
            stmt.addBatch(sb.toString());
            if (k > commitlength) {
                k = 0;
                stmt.executeBatch();
                cn.commit();
            }
        }
        stmt.executeBatch();
        cn.commit();
        stmt.close();
    }

    static void updatePrepared(int count, int commitlength) throws SQLException {
        PreparedStatement pstmt = cn.prepareStatement("insert into ZH(zh,product,state) values (?,?,?)");
        int k = 0;
        for (int i = 0; i < count; i++) {
            k++;
            String zh = "zh" + i;
            String pro = "pro" + i;
            String state = "state" + i;
            pstmt.setString(1, zh);
            pstmt.setString(2, pro);
            pstmt.setString(3, state);
            pstmt.addBatch();
            if (k > commitlength) {
                k = 0;
                pstmt.executeBatch();
                cn.commit();
                pstmt.clearBatch();
            }
        }
        pstmt.executeBatch();
        cn.commit();
        pstmt.close();
    }

    static void clear() throws SQLException {
        String sqlhead = "delete from ZH";
        Statement stmt = cn.createStatement();
        stmt.executeUpdate(sqlhead);
        cn.commit();
        stmt.close();
    }
}
0 请登录后投票
   发表时间:2011-05-08  
darklighting_he 写道

就我的测试来看,PreparedStatement 和 拼装的SQL性能差距巨大,性能差别可以达到100倍.
测试环境:
服务器:E7300,3G,win2003 R2 SP2,Oracle11R2
客户端:一般100M网络与服务器连接,jdbc驱动ojdbc6

测试结果
测试:数据长度[100],每10提交一次
第一步清理耗时:140ms
第一次一般提交耗时:130ms
第二步清理耗时:0ms
第一次Prepared提交耗时:70ms
第二步清理耗时:0ms
第二次Prepared提交耗时:20ms
第三步清理耗时:0ms
第二次一般提交耗时:100ms
-----------------------------------------------
测试:数据长度[1000],每100提交一次
第一步清理耗时:0ms
第一次一般提交耗时:1140ms
第二步清理耗时:10ms
第一次Prepared提交耗时:40ms
第二步清理耗时:10ms
第二次Prepared提交耗时:40ms
第三步清理耗时:10ms
第二次一般提交耗时:850ms
-----------------------------------------------
测试:数据长度[10000],每1000提交一次
第一步清理耗时:20ms
第一次一般提交耗时:10540ms
第二步清理耗时:80ms
第一次Prepared提交耗时:80ms
第二步清理耗时:80ms
第二次Prepared提交耗时:60ms
第三步清理耗时:80ms
第二次一般提交耗时:10530ms
-----------------------------------------------
测试:数据长度[100000],每1000提交一次
第一步清理耗时:80ms
第一次一般提交耗时:109780ms
第二步清理耗时:750ms
第一次Prepared提交耗时:660ms
第二步清理耗时:830ms
第二次Prepared提交耗时:600ms
第三步清理耗时:1970ms
第二次一般提交耗时:109800ms
-----------------------------------------------


注意看你的代码, 你这样比较并不公平. 而且, 很显然, 你并没有明白我的意思.

我再说一下 addbatch 和 statement 两者的区别(主要是指针对上面两个例子来说的).

你的两个例子其实都是用的 addbatch! 只是其中一种使用的直接拼接, 而另一种是用的 预编译 的方式.

使用预编译, 在第一次运行的时候会耗费大量的时间, 它的性能体现在后面的重复执行. 当一条 SQL 语句, 只在每次执行时的值不同时, 使用 预编译无疑是很快的.

但是, 我说的 statment 并不是这个意思!

我说的意思是这样: 拼接 SQL, 将多个数值拼成一条 SQL 进行执行, 如 insert into XXX(id, version) values ('', ''),('', ''),('', ''),('', ''),('', '')..., 这里拼出来的只有一条 SQL, 但将这条语句放入 db 去执行, 会插入多条数据. 这一点不难想像, 其效率远比 addbatch 要快


// 从开始到最后, 我只有这一个 StringBuilder 对象, 每次生成一条SQL, 数据库执行时却插入了多条, 然后又开始拼接 SQL...
StringBuilder sql = new StringBuilder();
String str = "INSERT INTO T_USERINFO(CREATE_TIME, ID) VALUES ";
sql.append(str);
for (int i = 1; i < (num + 1); i++) {
	// 要保证公平, 也在循环中 new 对象
	user = new UserInfo();
	
	sql.append("('").append(user.getCreateTime());
	sql.append("', '");
	sql.append(user.getId()).append("'),");

	if (i % 50 == 0) {
		// 执行并提交至数据库. 只执行一条 SQL, 结果却会在数据库插入多条数据.
		// 并没有使用 addbatch! addbatch 时使用预编译, 这一点我还是知道的...
		st.execute(sql.deleteCharAt(sql.length() - 1).toString());
		conn.commit();
		
		// 重新开始拼接字符串
		sql.delete(str.length(), sql.length());
	}
}
st.execute(sql.deleteCharAt(sql.length() - 1).toString());
conn.commit();
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics