GBase8a JDBC接口开启rewriteBatchedStatements=true参数后insert values攒批失败问题
1.问题场景
GBase 8a MPP Cluster作为一款分析型列存数据库,在单条insert values的场景下性能不高,通常建议客户从如下几个方面进行此类场景的优化:
- 将insert values转换为批量的load操作;
- 调用JDBC接口下发insert values时,应用将一组插入的values值进行拼接,拼成一个insert into [表名] (字段名1, 字段名2, 字段名3 …) values (),(),()…的sql串并通过JDBC接口一次性下发给GBase 8a,这样这一组值将通过8a的一次事务提交写入,避免了insert values 单个值一个一个写入提交造成的性能问题;
- 利用GBase8a提供的JDBC API中的rewriteBatchedStatements=true参数,开启JDBC驱动自动拼接insert values值的功能,并通过java代码中的addBatch和executeBatch方法,将一个batch中的多个insert values自动拼接成一个 insert values (),(),() … 串;
当采用第三种方案时,通常正确的配置和调用GBase8a提供的JDBC提供的方法即可起到自动拼接下发insert values串的效果,但有时会出现无法拼接成功的问题,本文将分析这种问题的原因并给出如何解决的方案。
2.代码示例
以下用一段java示例代码,printBatch.java来进行问题演示和剖析:
import java.sql.*;
import com.gbase.jdbc.StatementImpl;
import com.gbase.jdbc.Driver;
public class printBatch {
public static void main(String[] args) {
printBatch connectionSimple = new printBatch();
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
ResultSetMetaData rsmd = null;
try {
//Class.forName("com.gbase.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:gbase://192.168.56.161:5258/test?user=root&password=&rewriteBatchedStatements=true");
//conn = DriverManager.getConnection("jdbc:gbase://192.168.56.161:5258/test?user=root&password=");
//insert sql
String sql = "insert into ins_t4 (seq_id, select_content) values (?,?)";
//String printSql = sql;
//prepare statement
PreparedStatement stm = conn.prepareStatement(sql);
//fill batch
for (int i=0; i<100; i++) {
stm.setObject(1,i);
stm.setString(2,"aaa");
stm.addBatch();
//System.out.println(stm.toString());
}
//System.out.println(stm.toString());
//execute batch
stm.executeBatch();
} catch (SQLException ex) {
System.out.println("SQLException: " + ex.getMessage());
System.out.println("SQLState: " + ex.getSQLState());
System.out.println("VendorError: " + ex.getErrorCode());
}
finally {
try {
conn.close();
} catch (NullPointerException e) {
} catch (Exception e) {
conn = null;
}
}
}
}
1)代码中在连接数据库的连接串 conn = DriverManager.getConnection("jdbc:gbase://192.168.56.161:5258/test?user=root&password=&rewriteBatchedStatements=true") 中打开了rewriteBatchedStatements=true开关,开启JDBC接口自动攒批功能
2)初始化了插入sql的代码String sql = “insert into ins_t1 (seq_id, `select_content`) values (?,?)”
3)用一个循环将实际values的值替换sql串中的“?”占位符,并调用addBatch和executeBatch方法进行攒批和执行
for (int i=0; i<100; i++) {
stm.setObject(1,i);
stm.setString(2,"aaa");
stm.addBatch();
//System.out.println(stm.toString());
}
//System.out.println(stm.toString());
//execute batch
stm.executeBatch();正常情况下开启了rewriteBatchedStatements=true开关后,JDBC接口会将Batch中的一批insert values语句自动拼接成一个 insert … values(),(),()…的字符串,并下发给管理节点,我们使用了集群测试环境作为验证,具体环境如下:
管理节点IP地址:192.168.56.161
测试库:test
测试表:ins_t4
测试表结构:
CREATE TABLE ins_t4 (
seq_id int(11) DEFAULT NULL,
select_content varchar(20) DEFAULT NULL
)
执行上述java测试代码时,开启集群的general_log,抓取java程序下发给集群的SQL,如下:

发现下发的SQL并未像预期的形成insert into … values (),(),()这样的串,而是下发了单条的insert values,即攒批失败了。
2.问题原因排查
咨询GBase负责JDBC接口的研发工程师,了解到rewriteBatchedStatements=true参数开启后,如果原始的insert values语句中包含了select关键字则可能造成JDBC攒批拼串失败,因JDBC代码中对insert类型的语句能否攒批进行了判断,对带select关键字的sql,会判断为insert select类型sql,不满足insert values拼接条件。处理这种情况,需要将原始insert values串中带select关键字的部分用``进行包围。
检查测试表结构:
CREATE TABLE ins_t4 (
seq_id int(11) DEFAULT NULL,
select_content varchar(20) DEFAULT NULL
)
字段名select_content中带有select关键字,且代码中的下发的insert语句模版如下:
String sql = "insert into ins_t4 (seq_id, select_content) values (?,?)";
未对select关键字进行包围处理,应该是造成拼接失败的原因;
3.问题处理办法
修改代码中的原始insert sql的模版,将select_content字段名用``进行包围,修改部分代码如下:
String sql = "insert into ins_t4 (seq_id, `select_content`) values (?,?)";
再次执行java代码中的逻辑,观察general_log中下发至管理节点的sql:

发现下发的sql已经按照预期进行了insert values (),(),()…类型的拼接
3.总结
在实际业务代码中,对于需要通过JDBC提供的rewriteBatchedStatements=true进行insert values拼接的场景,需要对执行的sql串中是否有select关键字进行检查,并对此类情况进行相应处理,包括修改表字段名称,不使用select关键字,或在java代码中对带select关键字的字段名添加``包围处理。
热门帖子
- 12025-12-01浏览数:182759
- 22023-05-09浏览数:25051
- 42023-09-25浏览数:18521
- 52020-05-11浏览数:17526