GBase 8a
性能调优
文章

GBase8a JDBC接口开启rewriteBatchedStatements=true参数后insert values攒批失败问题

发表于2024-04-14 11:23:22280次浏览1个评论

1.问题场景

GBase 8a MPP Cluster作为一款分析型列存数据库,在单条insert values的场景下性能不高,通常建议客户从如下几个方面进行此类场景的优化:

  1. 将insert values转换为批量的load操作;
  2. 调用JDBC接口下发insert values时,应用将一组插入的values值进行拼接,拼成一个insert into [表名] (字段名1, 字段名2, 字段名3 …) values (),(),()…的sql串并通过JDBC接口一次性下发给GBase 8a,这样这一组值将通过8a的一次事务提交写入,避免了insert values 单个值一个一个写入提交造成的性能问题;
  3. 利用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关键字的字段名添加``包围处理。

 

评论

登录后才可以发表评论
崔哥发表于 6个月前
黄河九天上,人鬼瞰重关。长风怒卷高浪,飞洒日光寒。峻似吕梁千仞,壮似钱塘八月,直下洗尘寰。万象入横溃,依旧一峰闲。仰危巢,双鹄过,杳难攀。人间此险何用,万古袐神奸。不用燃犀下照,未必佽飞强射,有力障狂澜。唤取骑鲸客,挝鼓过银山。