一文带你搞懂mysql中的三种数据读取方式

 更新时间:2024年12月27日 08:28:58   作者:HBLOG  
在与MySQL数据库交互时,数据的读取方式有多种选择,每种方式都有其独特的原理、优势和劣势,本文将对这三种读取方式进行详细介绍,需要的可以参考下

在与MySQL数据库交互时,数据的读取方式有多种选择,包括流式读取、游标读取和普通读取。每种方式都有其独特的原理、优势和劣势。本文将对这三种读取方式进行详细介绍,

1. 普通读取

介绍

普通读取是指通过JDBC的StatementPreparedStatement执行SQL查询,JDBC驱动会阻塞的一次性读取全部查询的数据到 JVM 内存中。这种方式适用于小型数据集的读取。

原理

在普通读取中,当执行查询时,JDBC会将整个结果集从数据库加载到内存中。开发者可以通过ResultSet对象逐行访问数据。

示例代码

Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;

try {
    String url = "jdbc:mysql://localhost:3307/test?useSSL=false";
    connection = DriverManager.getConnection(url, user, password);
    statement = connection.createStatement();
    resultSet = statement.executeQuery("SELECT * FROM table_name");

    while (resultSet.next()) {
        System.out.println(resultSet.getString("column_name"));
    }
} catch (SQLException e) {
    e.printStackTrace();
} finally {
    // close
    if (resultSet != null) resultSet.close();
    if (statement != null) statement.close();
    if (connection != null) connection.close();
}

优势

  • 简单易用:代码结构简单,易于理解和使用。
  • 适合小数据集:对于小型数据集,性能良好,读取速度快。

劣势

  • 内存消耗:对于大型数据集,可能导致内存消耗过大,甚至引发OutOfMemoryError
  • 不适合实时处理:无法实时处理数据,需等待整个结果集加载完成。

2. 游标读取

介绍

游标读取是指通过JDBC的StatementPreparedStatement使用游标逐行读取数据。游标允许在结果集中移动,适合处理较大的数据集。

原理

游标读取通过在数据库中维护一个指向结果集的指针,允许逐行访问数据。每次读取一行数据,游标向前移动,直到结果集结束。

示例代码

在连接参数中需要拼接useCursorFetch=true;

创建Statement时需要设置ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY

设置fetchSize控制每一次获取多少条数据

Connection connection = null; 
PreparedStatement preparedStatement = null; 
ResultSet resultSet = null;

try { 
    String url ="jdbc:mysql://localhost:3307/test?useSSL=false&useCursorFetch=true"; 
    connection = DriverManager.getConnection(url, user, password); 
    preparedStatement = connection.prepareStatement("SELECT * FROM table_name", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); 
    preparedStatement.setFetchSize(100); 
    //set fetchSize 
    resultSet = preparedStatement.executeQuery();
    while (resultSet.next()) {
    System.out.println(resultSet.getString("column_name"));
        
    }

} catch (SQLException e) { 
    e.printStackTrace(); 
    
} finally { 
    // close reources 
    if (resultSet != null) 
    resultSet.close(); 
    if (preparedStatement != null) 
    preparedStatement.close(); 
    if (connection != null) 
    connection.close(); 
    
}

优势

  • 内存效率:只在内存中保留当前行,适合处理大型数据集。
  • 逐行处理:可以逐行读取和处理数据,适合实时数据处理场景。

劣势

  • 复杂性:相较于普通读取,代码结构稍复杂。
  • 性能开销:在某些情况下,逐行读取可能会导致性能下降。

游标查询需要注意的点:

由于MySQL方不知道客户端什么时候将数据消费完,而自身的对应表可能会有DML写入操作,此时MySQL需要建立一个临时空间来存放需要拿走的数据。因此对于当你启用useCursorFetch读取大表的时候会看到MySQL上的几个现象:

  • IOPS飙升 (IOPS (Input/Output Per Second):磁盘每秒的读写次数)
  • 磁盘空间飙升
  • 客户端JDBC发起SQL后,长时间等待SQL响应数据,这段时间就是服务端在准备数据
  • 在数据准备完成后,开始传输数据的阶段,网络响应开始飙升,IOPS由“读写”转变为“读取”。
  • CPU和内存会有一定比例的上升

3. 流式读取

介绍

流式读取是指通过JDBC的StatementPreparedStatement以流的方式读取数据,适合处理非常大的数据集。

原理

流式读取通过设置ResultSet的类型和并发模式,允许在不将整个结果集加载到内存的情况下,逐行读取数据。通常结合setFetchSize()方法来控制每次从数据库中获取的行数。

示例代码

Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;

try {
    String url = "jdbc:mysql://localhost:3307/test?useSSL=false";
    connection = DriverManager.getConnection(url, user, password);
    preparedStatement = connection.prepareStatement("SELECT * FROM table_name", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
    preparedStatement.setFetchSize(1000); // 设置每次读取的行数
    //OR USEING com.mysql.jdbc.StatementImpl
    //((StatementImpl) statement).enableStreamingResults();
    resultSet = preparedStatement.executeQuery();

    while (resultSet.next()) {
        System.out.println(resultSet.getString("column_name"));
    }
} catch (SQLException e) {
    e.printStackTrace();
} finally {
    // 关闭资源
    if (resultSet != null) resultSet.close();
    if (preparedStatement != null) preparedStatement.close();
    if (connection != null) connection.close();
}

优势

  • 极高的内存效率:适合处理超大数据集,内存占用极低。
  • 实时处理能力:可以实时处理数据,适合流式数据分析。

劣势

  • 复杂性:实现相对复杂,需要合理设置fetch size
  • 性能问题:在某些情况下,频繁的数据库访问可能导致性能下降。

流式查询应该注意的坑

WARN ] 2024-12-26 09:36:50.365 [] job-file-log-676bc326966a463e08520799 - [srtosr][sr35] - Query 'his_config_info_exp' snapshot row size failed: java.lang.RuntimeException: io.tapdata.flow.engine.V2.exception.node.NodeException: Query table 'his_config_info_exp' count failed: No operations allowed after connection closed.
java.util.concurrent.CompletionException: java.lang.RuntimeException: io.tapdata.flow.engine.V2.exception.node.NodeException: Query table 'his_config_info_exp' count failed: No operations allowed after connection closed.
 at java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:273)
 at java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:280)
 at java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1643)
 at java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1632)
 at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
 at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
 at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
 at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)

MySQL Connector/J 5.1 Developer Guide中原文:

There are some caveats with this approach. You must read all of the rows in the result set (or close it) before you can issue any other queries on the connection, or an exception will be thrown. 也就是说当通过流式查询获取一个ResultSet后,通过next迭代出所有元素之前或者调用close关闭它之前,不能使用同一个数据库连接去发起另外一个查询,否者抛出异常(第一次调用的正常,第二次的抛出异常)。

总结

在选择MySQL的数据读取方式时,需要根据具体的应用场景和数据集大小来决定:

  • 普通读取适合小型数据集,简单易用,但内存消耗较大。
  • 游标读取适合中型数据集,内存效率较高,逐行处理。
  • 流式读取适合超大数据集,内存占用极低,实时处理能力强,但实现复杂。

根据实际需求,选择合适的读取方式可以提高应用程序的性能和可扩展性。

到此这篇关于一文带你搞懂mysql中的三种数据读取方式的文章就介绍到这了,更多相关mysql数据读取内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • FROM_UNIXTIME 格式化MYSQL时间戳函数

    FROM_UNIXTIME 格式化MYSQL时间戳函数

    对MYSQL没有进行过深入的研究,基础知识匮乏,一遇到问题只能手册,看来要把MYSQL的学习安排进时间表了。
    2011-04-04
  • MySQL中怎么匹配年月

    MySQL中怎么匹配年月

    一般数据库中给到的时间都是年-月-日形式的,那怎么匹配年-月/的形式呢,下面通过实例代码介绍怎么在数据库中查询到关于2021年8月的数据,对mysql匹配年月相关知识,感兴趣的朋友跟随小编一起看看吧
    2024-04-04
  • MySQL多表连接的入门实例教程

    MySQL多表连接的入门实例教程

    这篇文章主要给大家介绍了关于MySQL多表连接的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用MySQL具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-12-12
  • MySQL全面瓦解之查询的过滤条件详解

    MySQL全面瓦解之查询的过滤条件详解

    这篇文章主要给打大家介绍了关于MySQL全面瓦解之查询的过滤条件的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-11-11
  • MySql批量插入优化Sql执行效率实例详解

    MySql批量插入优化Sql执行效率实例详解

    这篇文章主要介绍了MySql批量插入优化Sql执行效率实例详解的相关资料,需要的朋友可以参考下
    2017-04-04
  • 浅谈sql数据库去重

    浅谈sql数据库去重

    关于sql去重,我简单谈一下自己的理解和经验,如果各位有建议或有不明白的欢迎多多指出。
    2014-08-08
  • Windows免安装MySQL 8.0.28 版本图文教程

    Windows免安装MySQL 8.0.28 版本图文教程

    这篇文章主要为大家详细介绍了Windows免安装MySQL8.0.28版本图文教程,文中安装步骤介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-04-04
  • CentOS7下mysql 8.0.16 安装配置方法图文教程

    CentOS7下mysql 8.0.16 安装配置方法图文教程

    这篇文章主要为大家详细介绍了CentOS7下mysql 8.0.16 安装配置方法图文教程,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-05-05
  • 千万级记录的Discuz论坛导致MySQL CPU 100%的优化笔记

    千万级记录的Discuz论坛导致MySQL CPU 100%的优化笔记

    谈到自己在解决一个拥有 60 万条记录的 MySQL 数据库访问时,导致 MySQL CPU 占用 100% 的经过。在解决问题完成优化(optimize)之后,我发现 Discuz 论坛也存在这个问题,当时稍微提了一下
    2010-12-12
  • Mysql中文汉字转拼音的实现(每个汉字转换全拼)

    Mysql中文汉字转拼音的实现(每个汉字转换全拼)

    这篇文章主要介绍了Mysql中文汉字转拼音的实现,并且每个汉字会转换全拼,使用Mysql自定义函数实现,需要的朋友可以参考下
    2014-06-06

最新评论