Azure:对于预准备语句,每个会话的内存限制超过20 MB

时间:2017-05-25 19:17:27

标签: java azure apache-pig azure-sql-database

我正在执行很多批次,包含准备好的insert语句

public static void main(String... args) throws Exception {
    Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
    BufferedReader csv = new BufferedReader(new InputStreamReader(Main.class.getClassLoader().getResourceAsStream("records.csv")));
    String line;
    createConnectionAndPreparedStatement();
    while ((line = csv.readLine()) != null) {
        tupleNum++;
        count++;
        List<String> row = new ArrayList<String>(Arrays.asList(line.split(";")));

        tupleCache.add(row);
        addBatch(row, ps);
        if (count > BATCH_SIZE) {
            count = 0;
            executeBatch(ps);
            tupleCache.clear();
        }
    }
}

protected static void createConnectionAndPreparedStatement() throws SQLException {
    System.out.println("Opening new connection!");
    con = DriverManager.getConnection(jdbcUrl, jdbcUser, jdbcPassword);
    con.setAutoCommit(true);
    con.setAutoCommit(false);
    ps = con.prepareStatement(insertQuery);

    count = 0;
}


private static void executeBatch(PreparedStatement ps) throws SQLException, IOException, InterruptedException {
    try {
        ps.executeBatch();
    } catch (BatchUpdateException bue) {
        if (bue.getMessage() != null && bue.getMessage().contains("Exceeded the memory limit")) {
            // silently close the old connection to free resources
            try {
                con.close();
            } catch (Exception ex) {}
            createConnectionAndPreparedStatement();
            for (List<String> t : tupleCache) {
                addBatch(t, ps);
            }
            // let's retry once
            ps.executeBatch();
        }
    }
    System.out.println("Batch succeeded! -->" + tupleNum );
    con.commit();
    ps.clearWarnings();
    ps.clearBatch();
    ps.clearParameters();
}

private static void addBatch(List<String> tuple, PreparedStatement ps) throws SQLException {
    int sqlPos = 1;
    int size = tuple.size();
    for (int i = 0; i < size; i++) {
        String field = tuple.get(i);
        //log.error(String.format("Setting value at pos [%s] to value [%s]", i, field));
        if (field != null) {
            ps.setString(sqlPos, field);
            sqlPos++;
        } else {
            ps.setNull(sqlPos, java.sql.Types.VARCHAR);
            sqlPos++;
        }
    }
    ps.addBatch();
}

因此,在独立应用程序中,一切都很好,700k批量插入后不会发生异常。但是当我在大约6-7k批量插入后在自定义猪StoreFunc中执行实际相同的代码时,我得到以下异常:

java.sql.BatchUpdateException: 112007;Exceeded the memory limit of 20 MB per session for prepared statements. Reduce the number or size of the prepared statements.
    at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement.executeBatch(SQLServerPreparedStatement.java:1824)

只有重新启动连接才有帮助。有人可以帮助我解决为什么会发生这种情况以及如何解决这个问题吗?

2 个答案:

答案 0 :(得分:2)

根据你的描述&amp;错误信息,根据我的经验,我认为问题是由SQL Azure服务器端的内存配置引起的,例如服务器资源池内连接的内存限制。

我试图按照线索搜索关于连接内存限制的具体解释,但是失败了,除了here下面的内容。

  

连接记忆

     

SQL Server为客户端的每个连接留出三个数据包缓冲区。根据sp_configure存储过程指定的默认网络包大小调整每个缓冲区的大小。如果默认网络数据包大小小于8KB,则这些数据包的内存来自SQL Server的缓冲池。如果它是8KB或更大,则从SQL Server的MemToLeave区域分配内存。

我继续搜索packet size&amp; MemToLeave并查看它们。

基于以上信息,我猜“超出预准备语句每个会话20 MB的内存限制”表示在SQL Azure实例的最大内存缓冲池上使用并行连接的所有内存。

所以我建议你尝试两种解决方案。

  1. 建议减少BATCH_SIZE变量的值,使服务器内存开销小于内存缓冲池的最大大小。
  2. 尝试扩展SQL Azure实例。
  3. 希望它有所帮助。

    以下是两条新建议。

    1. 我真的不确定MS jdbc驱动程序是否支持使用Apache Pig执行此操作的当前场景,就像并行ETL作业一样。请尝试使用jtds jdbc驱动程序而不是MS。
    2. 我认为更好的方法是使用更专业的工具来执行此操作,例如sqoopkettle

答案 1 :(得分:0)

当我尝试将熊猫数据帧写入Azure SQL数据仓库时,我遇到了同样的问题。我指定了块大小,并为负载用户分配了最大的资源类。但是,问题仍然存在。

根据文档,默认情况下,INSERT VALUE语句仅使用smallrc resource class

我唯一想到的解决方案是扩大DWU,但这不是最佳解决方案,因为成本会很高。