项目中遇到在做导入号码的时候我们会用到批量导入,提高入库的速度。接下来我们以10000条为测试用例。
当需要成批插入或者更新记录时,可以采用Java的批量更新机制,这一机制允许多条语句一次性提交给数据库批量处理。通常情况下比单独提交处理更有效率
JDBC的批量处理语句包括下面三个方法:
- addBatch(String):添加需要批量处理的SQL语句或是参数;
- executeBatch():执行批量处理语句;
- clearBatch():清空缓存的数据
通常我们会遇到两种批量执行SQL语句的情况:
- 多条SQL语句的批量处理;
- 一个SQL语句的批量传参;
phoneListService.saveBatch(callLists);
感觉插入还是有些慢,查了下文档,需要增加一个参数
Postgres jdbc的连接:
url: jdbc:postgresql://102.2.1.21:5432/postgres?
binaryTransfer=false&forceBinary=false&reWriteBatchedInserts=true
Mysql jdbc连接:
jdbc:mysql://102.2.1.21:3306/demo?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B1&rewriteBatchedStatements=true
增加以下的参数进行优化
rewriteBatchedStatements=true
:控制是否将批量插入语句转换成更高效的形式,true
表示转换,默认为false
binaryTransfer=false
:控制是否使用二进制协议传输数据,false
表示不适用,默认为true
。
forceBinary=false
:控制是否将非 ASCII 字符串强制转换为二进制格式,false
表示不强制转换,默认为true
感觉还是慢,不过不知道是什么原因。后来该用jdbc原生的,每500提交一次
Connection conn = jdbcUtils.getConnection();
PreparedStatement ps = null;
try {
ps = jdbcUtils.createPreparedStatement1(conn, sql);
//取消自动提交
conn.setAutoCommit(false);
for (int i = 0; i < phoneList.size(); i++) {
PhoneList call = phoneList.get(i);
Long startTime = DateUtils.getSec("00:00");
String date1 = phone.getTodayBegin();
String date2 = phone.getTodayStop();
logger.warn("======date1:{} date2:{}", date1, date2);
if (StringUtil.isNotEmpty(date1)) {
startTime = DateUtils.getSec(date1.substring(date1.indexOf(":") - 2));
}
long endTime = DateUtils.getSec("23:58");
if (StringUtil.isNotEmpty(date2)) {
endTime = DateUtils.getSec(date2.substring(date2.indexOf(":") - 2));
}
logger.warn("======startTime:{} endTime:{}", startTime, endTime);
if (StringUtils.isNotEmpty(sql)) {
String startTimeParam = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, phone.getStartTime());
String endTimeParam = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, phone.getEndTime());
ps = jdbcUtils.createPreparedStatement2(ps, call.getId(), 2, 1, 0, phone.getRetryCount(), startTime, endTime,
112, call.getId(), 0, phone.getPrefix() + call.getCustomerPhone(), 1, phone.getPrefix() + call.getCustomerPhone(),
1, phone.getName(), 101, phone.getCampaignId(), phone.getMark(), phone.getIvrProfileUrl(), startTimeParam, endTimeParam);
}
ps.addBatch();
if (i % 500 == 0) {
ps.executeBatch();
ps.clearBatch();
}
}
ps.executeBatch();
ps.clearBatch();
//所有语句都执行完毕后才手动提交sql语句
conn.commit();
} catch (SQLException e) {
logger.error("insertBatchSql error:{}", e);
} finally {
jdbcUtils.close(conn, ps);
}
jdbcutils工具,也可以自己实现
package com.gary.utils.jdbc;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
/**
* JDBC工具类
*
* @author byx
*/
public class JdbcUtils {
private final ConnectionManager connManager;
private static Logger logger = LoggerFactory.getLogger(JdbcUtils.class);
/**
* 创建JdbcUtils
*
* @param dataSource 数据源
*/
public JdbcUtils(DataSource dataSource) {
connManager = new ConnectionManager(dataSource);
}
public Connection getConnection() {
return connManager.getConnection();
}
public void close(Connection conn, PreparedStatement stmt) {
connManager.close(conn, stmt, null);
}
public PreparedStatement createPreparedStatement1(Connection conn, String sql) throws SQLException {
PreparedStatement stmt = conn.prepareStatement(sql);
return stmt;
}
public PreparedStatement createPreparedStatement2(PreparedStatement stmt, Object... params) throws SQLException {
for (int i = 0; i < params.length; ++i) {
stmt.setObject(i + 1, params[i]);
}
return stmt;
}
private PreparedStatement createPreparedStatement(Connection conn, String sql, Object... params) throws SQLException {
PreparedStatement stmt = conn.prepareStatement(sql);
for (int i = 0; i < params.length; ++i) {
stmt.setObject(i + 1, params[i]);
}
return stmt;
}
/**
* 查询数据库并转换结果集。
* 用户可自定义结果集转换器。
* 用户也可使用预定义的结果集转换器。
*
* @param sql sql语句
* @param resultMapper 结果集转换器
* @param params sql参数
* @param resultSetMapper返回的结果类型
* @return 成功则返回转换结果,失败则抛出DbException,结果为空则返回空列表
* @see ResultMapper
* @see ListResultMapper
* @see SingleRowResultMapper
*/
public T query(String sql, ResultMapper resultMapper, Object... params) {
ResultSet rs = null;
PreparedStatement stmt = null;
Connection conn = null;
try {
conn = connManager.getConnection();
logger.info("conn:{}", conn);
stmt = createPreparedStatement(conn, sql, params);
rs = stmt.executeQuery();
return resultMapper.map(rs);
} catch (SQLException e) {
throw new DbException(e.getMessage(), e);
} finally {
connManager.close(conn, stmt, rs);
}
}
/**
* 查询数据库,对结果集的每一行进行转换,然后将所有行封装成列表。
* 用户可自定义行转换器。
* 用户也可使用预定义的行转换器。
*
* @param sql sql语句
* @param rowMapper 行转换器
* @param params sql参数
* @param rowMapper返回的结果类型
* @return 成功则返回结果列表,失败则抛出DbException,结果为空则返回空列表
* @see RowMapper
* @see BeanRowMapper
* @see MapRowMapper
* @see SingleColumnRowMapper
*/
public List queryList(String sql, RowMapper rowMapper, Object... params) {
return query(sql, new ListResultMapper<>(rowMapper), params);
}
/**
* 查询数据库,将结果集的每一行转换成JavaBean,然后将所有行封装成列表。
*
* @param sql sql语句
* @param type JavaBean类型
* @param params sql参数
* @param JavaBean类型
* @return 成功则返回结果列表,失败则抛出DbException,结果为空则返回空列表
*/
public List queryList(String sql, Class type, Object... params) {
return query(sql, new ListResultMapper<>(new BeanRowMapper<>(type)), params);
}
/**
* 查询数据库,返回结果集中的单个值。
* 如果结果集中有多个值,则只返回第一行第一列的值。
*
* @param sql sql语句
* @param params sql参数
* @param 结果类型
* @return 成功则返回结果值,失败则抛出DbException,结果为空则返回null
*/
public T querySingleValue(String sql, Object... params) {
return query(sql, new SingleRowResultMapper<>(new SingleColumnRowMapper<>()), params);
}
/**
* 查询数据库,返回结果集中的单行数据。
* 如果结果集中有多行数据,则只返回第一行数据。
* 用户可自定义行转换器。
* 用户也可使用预定义的行转换器。
*
* @param sql sql语句
* @param rowMapper 行转换器
* @param params sql参数
* @param rowMapper返回的结果类型
* @return 成功则返回结果,失败则抛出DbException,结果为空则返回null
* @see RowMapper
* @see BeanRowMapper
* @see MapRowMapper
* @see SingleColumnRowMapper
*/
public T querySingleRow(String sql, RowMapper rowMapper, Object... params) {
return query(sql, new SingleRowResultMapper<>(rowMapper), params);
}
/**
* 查询数据库,将结果集中的单行数据转换成JavaBean。
*
* @param sql sql语句
* @param type JavaBean类型
* @param params sql参数
* @param JavaBean类型
* @return 成功则返回结果,失败则抛出DbException,结果为空则返回null
*/
public T querySingleRow(String sql, Class type, Object... params) {
return querySingleRow(sql, new BeanRowMapper<>(type), params);
}
/**
* 更新数据库,返回影响行数
*
* @param sql sql语句
* @param params sql参数
* @return 成功则返回影响行数,失败则抛出DbException
*/
public int update(String sql, Object... params) {
Connection conn = null;
PreparedStatement stmt = null;
try {
conn = connManager.getConnection();
stmt = createPreparedStatement(conn, sql, params);
return stmt.executeUpdate();
} catch (Exception e) {
throw new DbException(e.getMessage(), e);
} finally {
connManager.close(conn, stmt, null);
}
}
/**
* 开启事务
*/
public void startTransaction() {
connManager.startTransaction();
}
/**
* 提交事务
*/
public void commit() {
connManager.commit();
}
/**
* 回滚事务
*/
public void rollback() {
connManager.rollback();
}
/**
* 判断当前是否在事务中
*/
public boolean inTransaction() {
return connManager.inTransaction();
}
}
Datasource实现
package com.system.modules.utils.jdbc;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class JdbcDataSource {
private static Logger logger = LoggerFactory.getLogger(JdbcDataSource.class);
// 1. 声明静态数据源成员变量
private static DataSource ds;
// 2. 创建连接池对象
static {
logger.info("===数据库初始化====");
// 加载配置文件中的数据
InputStream is = JdbcUtils.class.getClassLoader().getResourceAsStream("durid.properties");
Properties pp = new Properties();
try {
pp.load(is);
logger.info("===数据库初始化====:{}",pp);
// 创建连接池,使用配置文件中的参数
ds = DruidDataSourceFactory.createDataSource(pp);
} catch (IOException e) {
logger.info("===数据库初始化====error:{}",e);
} catch (Exception e) {
logger.info("===数据库初始化====Exception:{}",e);
}
}
// 3. 定义公有的得到数据源的方法
public static DataSource getDataSource() {
return ds;
}
}
durid.propertes 实现:
driverClassName=org.postgresql.Driver url=jdbc:postgresql://100.2.13.2:5432/postgres?reWriteBatchedInserts=true username=root password=123456 initialSize=5 maxActive=50 maxWait=3000
执行时间:
看着速度快了许多。
接下来还要研究下,mybatisplus为什么还是慢。