精华帖 (2) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (1)
|
|
---|---|
作者 | 正文 |
发表时间:2007-01-22
其中的原因有好多,比如可以防止SQL注入攻击,防止数据库缓冲池溢出,代码的可读性,可维护性。这些都很正确。 但是还有一点人们经常提的就是PreparedStatement能够显著的提高执行效率。 看了两篇关于PreparedStatement和Statement的帖子 http://dev2dev.bea.com.cn/bbs/thread.jspa?forumID=121&threadID=10397&start=0&tstart=0 http://www.iteye.com/topic/5631?page=1 里面提到,PreparedStatement并不一定比Statement快,于是想自己动手测试一下 一共分5种情况测试 stmt pre-stmt --1:每次都单独连数据库--100条 21.936 22.076 22.05 22.051 22.172 22.178 --2:Stmt使用相同的SQL语句,参数值也相同--100条 0.78 0.796 0.718 0.749 0.749 0.764 --3:Stmt使用相同的SQL语句,参数值不同--100条 0.967 0.749 0.78 0.874 0.749 0.749 --4:Stmt使用相同的SQL语句,参数值也相同--10000条 33.079 36.154 34.156 39.306 34.359 36.138 --5:Stmt使用相同的SQL语句,参数值不同--10000条 32.799 34.125 32.564 35.53 35.701 34.952 32.582 40.798 38.893 42.345 35.082 41.736 分析: 第一种情况: 由于我测试时采用jdbc直接链接数据库,所以在第一种情况下插入速度很慢,时间都消耗在了数据库链接的创建上了,符合常理,没什么疑义。 第二种情况: 由于使用Statement插入时sql语句都相同,我通过阅读上面的两篇帖子,猜想可能是Statement语句也被DB缓存了,所以跟 PreparedStatement速度差不多,我想如果每次插入的sql都不同,那么Statement应该慢下来了吧。 第三种情况: 并非像我在执行第二种情况时想象的那样,PreparedStatement还是跟Statement差不多,于是我想是不是数据量不够大呀,于是我增大了 插入数据量,希望PreparedStatement能够比Statement快些 第四/五种情况: 测试结果PreparedStatement反到更慢了。 PreparedStatement什么情况下才会比Statement快?那位大侠来指点一下! 代码如下: DDL CREATE TABLE dbo.jdbc_stmt ( id numeric(10,0) IDENTITY, name varchar(10) NOT NULL, grad int NOT NULL, age int NOT NULL, dept int NOT NULL ) 测试程序 public void jdbcStmtPerformance() { //1:每次都单独连数据库--100条 // String stmtSQL = "insert into jdbc_stmt values('ivanl',2,25,1001)"; // for(int i = 0; i< 100; i++) // { // logger.debug("stmt #"+i); // getJdbcTemplate().update(stmtSQL); // } //2:Stmt使用相同的SQL语句,参数值也相同--100条 // getJdbcTemplate().execute(new StatementCallback(){ // public Object doInStatement(Statement stmt) throws SQLException // { // String stmtSQL = "insert into jdbc_stmt values('ivanl',2,25,1001)"; // for(int i = 0; i< 100; i++) // { // logger.debug("stmt #"+i); // stmt.executeUpdate(stmtSQL); // } // return null; // } // }); //3:Stmt使用相同的SQL语句,参数值不同--100条 // getJdbcTemplate().execute(new StatementCallback(){ // public Object doInStatement(Statement stmt) throws SQLException // { // String stmtSQL = "insert into jdbc_stmt values('ivanl',2,25,"; // for(int i = 0; i< 100; i++) // { // logger.debug("stmt #"+i); // stmt.executeUpdate(stmtSQL+i+")"); // } // return null; // } // }); // 4:Stmt使用相同的SQL语句,参数值也相同--10000条 // getJdbcTemplate().execute(new StatementCallback(){ // public Object doInStatement(Statement stmt) throws SQLException // { // String stmtSQL = "insert into jdbc_stmt values('ivanl',2,25,1001)"; // for(int i = 0; i< 10000; i++) // { // logger.debug("stmt #"+i); // stmt.executeUpdate(stmtSQL); // } // return null; // } // }); // 5:Stmt使用相同的SQL语句,参数值不同--10000条 getJdbcTemplate().execute(new StatementCallback(){ public Object doInStatement(Statement stmt) throws SQLException { String stmtSQL = "insert into jdbc_stmt values('ivanl',2,25,"; for(int i = 0; i< 10000; i++) { logger.debug("stmt #"+i); stmt.executeUpdate(stmtSQL+i+")"); } return null; } }); } public void jdbcPreStmtPerformance() { //1:每次都单独连数据库--100条 // String stmtSQL = "insert into jdbc_stmt values(?,?,?,?)"; // for(int i = 0; i< 100; i++) // { // logger.debug("pre-stmt #"+i); // getJdbcTemplate().update(stmtSQL, new Object[]{"ivanl", new Integer(2), new Integer(25), new Integer(1002)}); // } //2:Stmt使用相同的SQL语句,参数值也相同--100条 // String stmtSQL = "insert into jdbc_stmt values(?,?,?,?)"; // getJdbcTemplate().execute(stmtSQL,new PreparedStatementCallback(){ // public Object doInPreparedStatement(PreparedStatement ps) throws SQLException // { // for(int i = 0; i< 100; i++) // { // logger.debug("pre-stmt #"+i); // ps.setString(1, "ivanl"); // ps.setInt(2, 2); // ps.setInt(3, 25); // ps.setInt(4, 1002); // ps.execute(); // } // return null; // } // }); //3:Stmt使用相同的SQL语句,参数值不同--100条 // String stmtSQL = "insert into jdbc_stmt values(?,?,?,?)"; // getJdbcTemplate().execute(stmtSQL,new PreparedStatementCallback(){ // public Object doInPreparedStatement(PreparedStatement ps) throws SQLException // { // for(int i = 0; i< 100; i++) // { // logger.debug("pre-stmt #"+i); // ps.setString(1, "ivanl"); // ps.setInt(2, 2); // ps.setInt(3, 25); // ps.setInt(4, i); // ps.execute(); // } // return null; // } // }); //4:Stmt使用相同的SQL语句,参数值也相同--10000条 // String stmtSQL = "insert into jdbc_stmt values(?,?,?,?)"; // getJdbcTemplate().execute(stmtSQL,new PreparedStatementCallback(){ // public Object doInPreparedStatement(PreparedStatement ps) throws SQLException // { // for(int i = 0; i< 10000; i++) // { // logger.debug("pre-stmt #"+i); // ps.setString(1, "ivanl"); // ps.setInt(2, 2); // ps.setInt(3, 25); // ps.setInt(4, 1002); // ps.execute(); // } // return null; // } // }); //5:Stmt使用相同的SQL语句,参数值不同--10000条 String stmtSQL = "insert into jdbc_stmt values(?,?,?,?)"; getJdbcTemplate().execute(stmtSQL,new PreparedStatementCallback(){ public Object doInPreparedStatement(PreparedStatement ps) throws SQLException { for(int i = 0; i< 10000; i++) { logger.debug("pre-stmt #"+i); ps.setString(1, "ivanl"); ps.setInt(2, 2); ps.setInt(3, 25); ps.setInt(4, i); ps.execute(); } return null; } }); } 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2007-01-22
PreparedStatement的目的主要是批处理,跟Statement比顺序执行的效率本来就是不合理的测试方式
|
|
返回顶楼 | |
发表时间:2007-01-22
PreparedStatement预编译应该是在数据库server端吧。是不是要close Statement 和 PreparedStatement来测试。因为如果都是使用同一个stmt的话, 就没有考虑到编译的效率了。
|
|
返回顶楼 | |
发表时间:2007-01-22
这两个方法是使用junit分别测试的,使用的不是同一个stmt
|
|
返回顶楼 | |
发表时间:2007-01-22
Ivan Li 写道 这两个方法是使用junit分别测试的,使用的不是同一个stmt
你误解我的意思了。 这个跟junit有什么关系 |
|
返回顶楼 | |
发表时间:2007-01-22
我也怀疑楼主的测试方法。
不过我按我的方法测试了一下, 使用select * from test where id=?的查询,而不是insert。 我在循环里关闭statement。 但每次都是statement快一些。 不过我以前的确用PreparedStatement替代Statement得到了数倍的性能提高, 那时的情况是:字段数量很大,数百个字段(表是自动创建的),单次需要插入数千条记录, 所有使用了批处理。即便都是批处理,此时PreparedStatement还是能快约6倍。 |
|
返回顶楼 | |
发表时间:2007-01-22
不同类型的数据库对此的优化程度可能是不同的;
单用户状态下和多用户状态下也是不同的。 |
|
返回顶楼 | |
发表时间:2007-01-22
我对PreparedStatement和Statement的性能测试了一下:
测试代码如下: Connection con = getOraConnection(); String sql = "select id,name from test where id="; String tempSql; int count = 1000; long time = System.currentTimeMillis(); for (int i = 0; i < count; i++) { Statement st = con.createStatement(); tempSql=sql+(int) (Math.random() * 100); st.executeQuery(tempSql); st.close(); } System.out.println("st cost:" + (System.currentTimeMillis() - time)); String psql = "select id,name from test where id=?"; time = System.currentTimeMillis(); for (int i = 0; i < count; i++) { int id=(int) (Math.random() * 100); PreparedStatement pst = con.prepareStatement(psql); pst.setBigDecimal(1, new BigDecimal(id)); pst.executeQuery(); pst.close(); } System.out.println("pst cost:" + (System.currentTimeMillis() - time)); con.close(); test表很简单,id int,name varchar(50). 对几种数据库和相应驱动程序进行测试,结果如下(ms): oracle: 1235 1109 MSSQL2000(JTDS):391 453 MSSQL2000(MS): 453 640 Mysql5: 391 891 PostgreSQL8.1: 1078 1047 结论: 1.单并发情况下,oracle--PostgreSQL--MSSQL--MySQL的性能依次增高; 2.MySQL不支持Prepared Statement特性,已在其驱动程序的文档中证实,所以在MySQL里使用 PreparedStatement的性能尤其低,可以比Statement慢一倍以上。 而MSSQL2000下,PreparedStatement比Statement慢, Oracle,PostgreSQL对它的支持最好,使用PreparedStatement性能比Statement高。 |
|
返回顶楼 | |
发表时间:2007-01-23
count大点然后Math.random()*100 换成 Math.random()*10000000结果会不会更明显?
对于prepared stmt和stmt,Oracle应该都缓存其编译后的stmt,缓存100个对它来说太易了 对这个测试来说,所有pre stmt只需要编译一个即可,而stmt只要id不同就得编译 |
|
返回顶楼 | |
发表时间:2007-01-23
楼上的,如果你把Statement st = con.createStatement();
和PreparedStatement pst = con.prepareStatement(psql); 都写在for循环外面,应该就更能看出谁快谁慢了。我在roacle里测了一下,查询5000条数据,PreparedStatement比Statement快一倍。 |
|
返回顶楼 | |