首页 > 数据库 >MySQL三种读取模式详解:普通、流式、游标

MySQL三种读取模式详解:普通、流式、游标

时间:2025-01-10 09:30:51浏览次数:3  
标签:preparedStatement 读取 MySQL resultSet 游标 流式 connection sql null

在与MySQL数据库交互时,数据的读取方式有多种选择,包括普通读取、流式读取和游标读取。每种方式都有其独特的原理、优势和劣势。本文将对这三种读取方式进行详细介绍,并通过示例代码展示它们的使用方法和运行结果。

1. 普通读取

介绍

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

原理

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

示例代码

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class NormalReadExample {
    public static void main(String[] args) {
        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");
            String sql = "SELECT * FROM table_name";
            preparedStatement = connection.prepareStatement(sql);
            resultSet = preparedStatement.executeQuery();

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

运行结果

执行上述代码后,将输出table_name表中column_name列的所有值。

优势

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

劣势

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

介绍

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

原理

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

示例代码

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class CursorReadExample {
    public static void main(String[] args) {
        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");
            String sql = "SELECT * FROM table_name";
            preparedStatement = connection.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
            preparedStatement.setFetchSize(10); // 设置每次获取多少条数据
            resultSet = preparedStatement.executeQuery();

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

运行结果

执行上述代码后,将逐行输出table_name表中column_name列的值。

优势

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

劣势

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

游标查询需要注意的点

  • 由于MySQL不知道客户端什么时候将数据消费完,而对应的表可能会有DML写入操作,此时MySQL需要建立一个临时空间来存放需要拿走的数据。因此,在启用useCursorFetch读取大表时,可能会看到MySQL的IOPS和磁盘空间飙升,客户端JDBC发起SQL后,长时间等待SQL响应数据。
  • 在数据准备完成后,开始传输数据的阶段,网络响应开始飙升,IOPS由“读写”转变为“读取”。CPU和内存会有一定比例的上升。
3. 流式读取

介绍

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

原理

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

示例代码

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class StreamingReadExample {
    public static void main(String[] args) {
        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");
            String sql = "SELECT * FROM table_name";
            preparedStatement = connection.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
            preparedStatement.setFetchSize(1000); // 设置每次读取的行数
            resultSet = preparedStatement.executeQuery();

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

运行结果

执行上述代码后,将逐行输出table_name表中column_name列的值,同时保持较低的内存占用。

优势

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

劣势

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

流式查询需要注意的坑

  • 当通过流式查询获取一个ResultSet后,在通过next()迭代出所有元素之前或者调用close()关闭它之前,不能使用同一个数据库连接去发起另外一个查询,否则会抛出异常。例如:
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.
  • MySQL Connector/J的开发者指南中也提到了这一点:“在读取完结果集的所有行(或关闭结果集)之前,不能在同一连接上发起其他查询,否则会抛出异常。”
总结

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

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

根据实际需求,选择合适的读取方式

标签:preparedStatement,读取,MySQL,resultSet,游标,流式,connection,sql,null
From: https://blog.csdn.net/m0_37643701/article/details/144964096

相关文章

  • 如何通过宝塔面板定时任务重启MySQL服务以保持稳定性和优化性能
    MySQL服务在长时间运行后,可能会因为各种原因导致连接不稳定或内存占用过大,影响网站和应用程序的性能。为了确保MySQL服务的稳定性和优化性能,可以通过宝塔面板设置定时任务来定期重启MySQL服务。以下是具体的操作步骤和建议:理解定时重启的好处:定期重启MySQL服务可以帮助清理缓存......
  • MySQL中的事务和事务隔离级别
    MySQL/MariaDB中的事务和事务隔离级别 分类: 数据库系列undefined官方手册:https://dev.mysql.com/doc/refman/5.7/en/innodb-locking-transaction-model.html1.事务特性事务具有ACID特性:原子性(A,atomicity)、一致性(C,consistency)、隔离性(I,isolation)、持久性(D,......
  • Mysql事务的奥秘:探索InnoDB事务原理与MVCC机制
    本文章示例是以mysql8.0版本事务是一组操作的集合,它是不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或者撤销操作请求。即这些操作要么同时成功,要么同时失败。事务四大特性特性描述原子性事务是不可分割的最小单元,要么全部成功,要么全部失败一......
  • MySql数据库操作
    查看所有数据库:SHOWDATABASES;创建数据库:CREATEDATABASEdatabase_name;删除数据库:DROPDATABASEdatabase_name;选择数据库:USEdatabase_name;查看当前数据库中的所有表:SHOWTABLES;创建表:CREATETABLEtable_name(  column1datatype,  column2datatype,......
  • MySQL死锁的产生、排查与解决
    目录查看当前事务、死锁信息模拟DDL插入模拟数据模拟死锁(需开启不同事务)会话1(窗口1):Alice给Bob转账会话2(窗口2):Bob给Alice转账会话1(窗口1):尝试更新Bob的账户会话2(窗口2):尝试更新Alice的账户PS:关闭事务,结束死锁分析information_schema中的锁信息SHOWENGINEINNODBSTAT......
  • mysql-笔记
    如果要添加多列一起的唯一约束,使用第二种创建方式,查看主外键,唯一,约束的语句都是相同的。默认情况下唯一约束不起名的话,以列名为约束名。外连接left|right确定谁为逻辑主表,会显示所有逻辑主表中的内容,从表没有则为空,外连接一定要设置主外键相等(与内连接不同)。any是多......
  • MySQL索引原理及慢查询优化12
    背景MySQL凭借着出色的性能、低廉的成本、丰富的资源,已经成为绝大多数互联网公司的首选关系型数据库。虽然性能出色,但所谓“好马配好鞍”,如何能够更好的使用它,已经成为开发工程师的必修课,我们经常会从职位描述上看到诸如“精通MySQL”、“SQL语句优化”、“了解数据库原理”等......
  • 【MySQL开发】MySQL 中 varchar(50) 和 varchar(500) 有何区别
    一、问题描述我们在设计表结构的时候,设计规范里面有一条如下规则:对于可变长度的字段,在满足条件的前提下,尽可能使用较短的变长字段长度。为什么这么规定?我在网上查了一下,主要基于两个方面基于存储空间的考虑基于性能的考虑网上说Varchar(50)和varchar(500)存储空间上......
  • MySQL 知识梳理
    目录1数据库1.1数据简单原理图1.2使用命令行窗口[连接MYSQL](https://so.csdn.net/so/search?q=%E8%BF%9E%E6%8E%A5MYSQL&spm=1001.2101.3001.7020)数据库1.3Mysql三层结构1.4SQL语句分类1.5创建、查看、删除和备份恢复数据库√2.Mysql数据类型......
  • Rocky Linux 9.5 安装 MySQL 8.0
    RockyLinux9.5安装MySQL8.0RockyLinux9.5 [root@netkiller~]#dnfinstall-ymysql-server[root@netkiller~]#systemctlenablemysqldCreatedsymlink/etc/systemd/system/multi-user.target.wants/mysqld.service→/usr/lib/systemd/system/mysqld.ser......