executeBatchedInserts相比executePreparedBatchAsMultiStatement的方式传输效率更好,因为一次请求只重复一次前面的insert table (c1,c2,c3)
mysql server 对请求报文的最大长度有限制,如果batch size 太大造成请求报文超过最大限制,mysql 驱动会内部按最大报文限制查分成多个报文 。所以要真正减少提交次数
还要检查下mysql server的max_allowed_packet 否则batch size 再大也没用.
mysql> show VARIABLES like '%max_allowed_packet%';+--------------------+-----------+| Variable_name | Value |+--------------------+-----------+| max_allowed_packet | 167772160 |+--------------------+-----------+1 row in set (0.00 sec)要想验证mysql 发送了正确的sql 有两种方式
1 抓包,下图是wireshark在 应用端抓包mysql的报文
2 另一个办法是在mysql server端开启general log 可以查看mysql收到的所有sql
3 在jdbc url上加上参数traceProtocol=true 或者profileSQL=true or autoGenerateTestcaseScript=true
性能测试对比
import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.SQLException;import com.alibaba.druid.pool.DruidDataSource;public class BatchInsert {public static void main(String[] args) throws SQLException {int batchSize = 1000; int insertCount = 1000;testDefault(batchSize, insertCount);testRewriteBatchedStatements(batchSize,insertCount);}private static void testDefault(int batchSize, int insertCount) throws SQLException {long start = System.currentTimeMillis();doBatchedInsert(batchSize, insertCount,"");long end = System.currentTimeMillis();System.out.println("default:" + (end -start) + "ms"); }private static void testRewriteBatchedStatements(int batchSize, int insertCount) throws SQLException { long start = System.currentTimeMillis();doBatchedInsert(batchSize, insertCount, "rewriteBatchedStatements=true");long end = System.currentTimeMillis();System.out.println("rewriteBatchedStatements:" + (end -start) + "ms"); }private static void doBatchedInsert(int batchSize, int insertCount, String mysqlProperties) throws SQLException { DruidDataSource dataSource = new DruidDataSource(); dataSource.setUrl("jdbc:mysql://ip:3306/test?" + mysqlProperties); dataSource.setUsername("name"); dataSource.setPassword("password");dataSource.init();Connection connection = dataSource.getConnection();PreparedStatement preparedStatement = connection.prepareStatement("insert into Test (name,gmt_created,gmt_modified) values (?,now(),now())");for (int i = 0; i < insertCount; i++) { preparedStatement.setString(1, i+" "); preparedStatement.addBatch(); if((i+1) % batchSize == 0) { preparedStatement.executeBatch(); } } preparedStatement.executeBatch();connection.close();dataSource.close(); }}网络环境ping测试延迟是35ms ,测试结果:
default:75525msrewriteBatchedStatements:914ms3. 批量更新
//Session是持久层操作的基础,相当于JDBC中的Connection 。
Session session = sessionFactory.openSession();
try{ //为保持事务的原子性,必须捕捉异常 。所有事务都放在这一代码块里 。
【Java架构-MYSQL大数据量下的操作与优化】//操作事务时(增、删、改)必须显式的调用Transaction,如果不启动Transaction,数据库不会有变化(默认:session.autoCommit=false) 。
Transaction tx = session.beginTransaction();
for(int i=0; i<=1000; i++){
Student stu = new Student(...);
session.save(stu);//set value to stu
//批量更新:为防止内存不足,分成每20个一批发送过去 。如果不是大批量更新,则不需要这样
if(i%20==0){
//强制内存中数据同步到mysql,sql打印出并执行,只是事务没有commit,其他的线程看不到
session.flush();
session.clear();
}
}
//transaction commit默认会自动flush(查询之前、事务提交时都会自动flush,之前手动flush只是为了内存考虑) 。
tx.commit();//提交事务,Hibernate不喜欢抛异常,如有需要,自己捕捉 。
//查询方法 。如果有必要,也可以用事务(调用Transaction)
String hql = "from Student s where s.stuNo like ? and s.Sal > ?";//Student是类而不是表
List list = session.createQuery(hql)
.setString(0, "a00_").setDouble(1, 3000.0)//设置HQL的第一二个问号取值
.list();//Hibernate里面,没有返回值的都默认返回List
StringBuffer sb = new StringBuffer();
for(Student st :(List<Student>)list){//(List<Student>)强制类型转换
sb.Append(st.getOid()+" "+st.getName()+"n");//拿到Student类里的属性
}
System.out.print(sb.toString());//直接打印sb也可以,它也是调用toString,但这样写效率更高
} catch (HibernateException e) {
e.printStackTrace();
推荐阅读
- Java性能优化-掌握JMH
- 手机2G 3G 4G 5G 通信基站架构演进
- 微服务架构下的分布式事务基础入门
- 微服务架构下:MySQL5.7新特性--官方高可用方案MGR介绍
- JAVA的反射和注解
- Java缓冲流、转换流、序列化流
- 关于JavaScript及其对抓取和索引的影响
- 5大Java自动化测试框架
- 全面解析Java日期时间API
- 35个可以飞快提高千倍效率的Java语言代码小技巧,你值得拥有!
