jdbc流stream读取超大数据

253 篇文章 5 订阅


这几天在做分库分表,涉及到数据迁移,然后设计的方案是使用mybatis将数据分批读取出来,每批大概40--50万条数据,然后将查询来的数据再insert进去,发现这个效率很低,程序有卡顿,而且客户端很容易OOM。

找了一下原因就是jdbc默认的读取数据的时候,会将要查询的数据一次性读到内存中,再通过resultSet循环读取出来,这样子40--50万条数据很容易就撑爆内存,然后调研了下发现,其实可以通过jdbc流的方式读取数据,这种方式可以将读取出来的每一条数据查询到客户端,再执行insert操作,这样机器负载就会轻很多,实际操作了下,确实是这样的

上代码

public class StreamTest {
 
    public static void main(String[] args) throws SQLException, InterruptedException {
        StreamTest streamTest = new StreamTest();
 
        System.out.println("---------------------------");
 
        streamTest.selectData("select * from t_item_base_snapshot");
 
        System.out.println("---------------------------");
 
        streamTest.selectDataStream("select * from t_item_base_snapshot");
    }
 
    public static Connection getSqlConnection() {
        
        String url = "";
        String user = "";
        String password = "";
        String driverClass = "com.mysql.jdbc.Driver";
 
        Connection connection = null;
        try {
            Class.forName(driverClass);
            Properties info = new Properties();
            info.setProperty("user", user);//property的key必须是"user"
            info.setProperty("password", password);//property的key必须是"password"
            connection = DriverManager.getConnection(url, info);
            return connection;
        } catch (ClassNotFoundException e) {
            System.out.println("Not found driver class, load driver failed.");
        } catch (SQLException e) {
            System.out.println("create db connection error.");
        }
        return null;
    }
 
 
    public void selectData(String sqlCmd) throws SQLException {
 
        Connection conn = null;
        PreparedStatement stmt = null;
        ResultSet rs = null;
 
        try {
 
            conn = getSqlConnection();
            stmt = conn.prepareStatement(sqlCmd);
            rs = stmt.executeQuery();
 
            try {
                while (rs.next()) {
                    try {
                        System.out.println("one:" + rs.getString(1) + "two:" + rs.getString(2) + "thrid:" + rs.getString(3));
                    } catch (SQLException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
 
        } finally {
 
        }
    }
 
 
    public void selectDataStream(String sqlCmd) throws SQLException {
 
        Connection conn = null;
        PreparedStatement stmt = null;
        ResultSet rs = null;
 
        try {
 
            conn = getSqlConnection();
 
            stmt = conn.prepareStatement(sqlCmd, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
            stmt.setFetchSize(Integer.MIN_VALUE);
 
            rs = stmt.executeQuery();
 
            try {
                while (rs.next()) {
                    try {
                        System.out.println("one:" + rs.getString(1) + "two:" + rs.getString(2) + "thrid:" + rs.getString(3));
                    } catch (SQLException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
 
        } finally {
 
        }
    }
 
}
查询的表中大概有10万条数据,分别运行main方法中的selectData和selectDataStream方法,发现selectData执行过程中程序会有卡顿,说明正在将符合条件的数据全部load进内存,然后一次展示出来;selectDataStream执行的时候没有卡顿,数据会快速的分批的查询出来;最重要的selectData占用的内存比selectDataStream高两三倍,cpu使用率也高了一些

DataStream运行情况:

selectDataStream运行情况:

具体使用stream来读取数据,只是需要在生成prepareStatement时加上ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY这两个参数,再设置一下FetchSize为Integer.MIN_VALUE就可以了,mysql判断是否开启流式读取结果就是看这三个条件forward-only,read-only,fatch size=Integer.MIN_VALUE有没有被设置

如果不设置(不使用stream),就是默认的,mysql会将查询出来的数据全部返回客户端后,再执行ResultSet的next方法,在数据全部返回之前,next方法是阻塞的,设置了之后(使用stream),ResultSet的next方法就会立即返回server端返回的数据,这样客户端就不用使用大量的内存存储返回的数据了

除此之外,insert数据的时候,也可以使用jdbc提供的批次insert的方法addBatch(),以提高效率

还有就是,不管何种操作mysql的方式,比如常用的mybatis,因为底层都是使用的jdbc,所以要想高效的使用mysql,还是要从jdbc着手,要不好多坑还是绕不过去的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你可以使用stream)来获取第一条和最后一条记录。具体的实现方式可能会根据你使用的编程语言和数据库系统而有所不同。以下是一个示例,演示如何使用来获取第一条和最后一条记录: 假设你正在使用Java编程语言和关系型数据库MySQL,你可以使用JDBCJava Database Connectivity)来连接数据库并执行查询操作。下面是一个示例代码片段: ```java import java.sql.*; public class Main { public static void main(String[] args) { try { // 连接数据库 Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/db_name", "username", "password"); // 创建查询语句 String query = "SELECT * FROM table_name ORDER BY id ASC"; // 根据id升序排序 // 执行查询 Statement statement = connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); statement.setFetchSize(Integer.MIN_VALUE); // 使用stream)方式获取数据 ResultSet resultSet = statement.executeQuery(query); // 获取第一条记录 if (resultSet.next()) { String firstRecord = resultSet.getString("column_name"); // 替换为你要获取的列名 System.out.println("第一条记录:" + firstRecord); } // 移动到最后一个记录 resultSet.last(); // 获取最后一条记录 String lastRecord = resultSet.getString("column_name"); // 替换为你要获取的列名 System.out.println("最后一条记录:" + lastRecord); // 关闭连接 resultSet.close(); statement.close(); connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } ``` 请根据你的实际情况修改数据库连接信息、查询语句以及列名等参数。此示例仅供参考,你需要根据自己的需求进行适当调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值