【数据库】聊聊数据库中的 fetchsize 参数

2024-01-18 01:04

本文主要是介绍【数据库】聊聊数据库中的 fetchsize 参数,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

聊聊数据库中的 fetchsize 参数

  • 1.介绍
  • 2.案例
  • 3.MySQL 中的 fetchsize
  • 4.Oracle 中的 fetchsize

1.介绍

在使用查询语句的时候,经常需要根据条件来进行查询得到最终的总记录条数,然后得到结果之后需要来进行处理。

场景:Java 端从数据库读取 100W 数据进行后台业务处理。

  • 常规实现 1:分页读取出来。缺点:需要排序后分页读取,性能低下。
  • 常规实现 2:一次性读取出来。缺点:需要很大内存,一般计算机不行。
  • 非常规实现:建立长连接,利用服务端游标,一条一条流式返回给 Java 端。
  • 非常规实现优化:JDBC 中有个重要的参数 fetchSize(它对业务实现无影响,即不会限制读取条数等),优化后可显著提升性能。

缺省时,驱动程序一次从查询里获取所有的结果。这样可能对于大的数据集来说是不方便的, 因此 JDBC 驱动提供了一个用于设置从一个数据库游标抽取若干行的 ResultSet 的方法。在连接的客户端这边缓冲了一小部分数据行,并且在用尽之后, 则通过重定位游标检索下一个数据行块。

setFetchSize 最主要是为了减少网络交互次数设计的。访问 ResultSet 时,如果它每次只从服务器上取一行数据,则会产生大量的开销。setFetchSize 的意思是当调用 rs.next 时,ResultSet 会一次性从服务器上取得多少行数据回来,这样在下次 rs.next 时,它可以直接从内存中获取出数据而不需要网络交互,提高了效率。 这个设置可能会被某些 JDBC 驱动忽略的,而且设置过大也会造成内存的上升。

2.案例

MySQL 中的 fetchsize 是指从结果集中获取行数据的大小。fetchsize 的大小直接影响到程序的性能和响应时间,因此是值得注意的。

在 MySQL 中,当执行 SELECT 语句时,会返回一定数量的数据行。fetchsize 就是表示从结果集中获取多少行数据的大小。

int fetchSize = 100;  //设置fetchsize的大小为100
Statement stmt = conn.createStatement();
stmt.setFetchSize(fetchSize); //将fetchsize的大小设置为100
ResultSet rs = stmt.executeQuery("SELECT * FROM users");

上面的代码中,fetchsize 的大小被设置为 100。当 ResultSet 对象被创建后,只会获取到前 100 条数据。当我们遍历 ResultSet 对象时,例如使用 rs.next(),在获取到第 100 条记录时,会自动从数据库中获取接下来的数据。由此,我们可以看出,fetchsize 对程序的性能和响应时间起到非常重要的作用。

fetchsize 设置的太小,会导致程序频繁地访问数据库,从而影响程序的性能;当 fetchsize 设置的太大,可能会导致内存不足,从而影响程序的响应时间。

因此,在设置 fetchsize 时,需要结合程序的具体需求和硬件设备的配置进行合理的调整。如果不确定 fetchsize 的大小该如何设置,可以先进行测试,不断调整 fetchsize 的大小,直到找到合适的配置。

3.MySQL 中的 fetchsize

默认情况下,MySQL 的 JDBC 驱动会一下子把所有 row 都读取下来,这在一般情况下是最优的,因为可以减少 Client - Server 的通信开销。但是这样也有一个问题,当数据库查询结果很大时,特别当不能全部放进内存时,就会产生性能问题。 本来,JDBC API 里在 Connection、Statement 和 ResultSet 上都有设置 fetchSize 的方法, 但是 MySQL 的 JDBC 驱动都不支持,无论你怎么设 fetchSize,ResultSet 都会一次性从 Server 读取数据。在 MySQL 的官方论坛上也有多个这样的问题,总结一下解决办法如下:

  • MySQL 版本在 5.0 以上,MySQL 的 JDBC 驱动更新到最新版本(至少 5.0 以上)
  • Statement 一定是 TYPE_FORWARD_ONLY 的,并发级别是 CONCUR_READ_ONLY(即创建 Statement 的默认参数)
  • 以下两句语句选一即可:
    • statement.setFetchSize(Integer.MIN_VALUE);
    • ((com.mysql.jdbc.Statement)stat).enableStreamingResults();

这样会一行一行地从 Server 读取数据,因此通信开销很大,但内存问题可以解决。官方的说法是不支持 fetchSize 不是 MySQL 的 JDBC 驱动的问题,而是 MySQL 本身就不支持。而商用数据库 Oracle 或 DB2 都是支持 fetchSize 的,从这里也可以看出两者的考量不同。

MySQL 默认是不支持 fetchSize 的,像 Oracle 和 DB2 等商业数据库则支持,那么这么在 JDBC 连接 MySQL 是使用这个参数呢,满足三个条件,,官方是这么说的

param
explain
value
useCursorFetchIf connected to MySQL > 5.0.2, and setFetchSize() > 0 on a statement, should that statement use cursor-based fetching to retrieve rows?false
  • 首先数据库版本要高于 5.0.2
  • 再就是设置了 fetchSize
  • 最最重要的是在数据库连接后加上,useCursorFetch=true
url=jdbc:mysql://192.168.1.3:3306/cuishou3?useCursorFetch=true

4.Oracle 中的 fetchsize

上面已经讲过了 Oracle 是默认支持的。

public static void main(String[] args) throws SQLException {getAll(1);getAll(10);getAll(100);getAll(1000);
}public static void getAll(int fetchSize) {try {long beginTime = System.currentTimeMillis();Connection connection = DriverManager.getConnection(MYSQL_URL);connection.setAutoCommit(false); //为了设置fetchSize,必须设置为falseString sql = "select * from test";PreparedStatement psst = connection.prepareStatement(sql,ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);psst.setFetchSize(fetchSize);ResultSet rs = psst.executeQuery();int totalCount = 0;// 从内存中来进行读取while (rs.next()) {totalCount++;}rs.close();psst.close();connection.close();long endTime = System.currentTimeMillis();System.out.println("totalCount:"+totalCount+";fetchSize:"+fetchSize+";耗时:"+(endTime-beginTime)+"ms");} catch (SQLException e) {e.printStackTrace();} 
}

执行结果如下所示:

totalCount:3185194;fetchSize:1;耗时:23770ms
totalCount:3185194;fetchSize:10;耗时:23253ms
totalCount:3185194;fetchSize:100;耗时:21890ms
totalCount:3185194;fetchSize:1000;耗时:20985ms

可以看到,当 fetchSize 为 1000 时,性能有提升(看一些网友的数据,性能提升更多)。

  • 先在服务端执行查询后将数据缓存在服务端(耗时相对较长)。
  • Java 端获取数据时,利用服务端游标进行指针跳动,如果 fetchSize 为 1000,则一次性跳动 1000 条,返回给 Java 端缓存起来(耗时较短,跳动次数为 N / 1000 N/1000 N/1000)。
  • 在调用 next 函数时,优先从缓存中取数,其次执行上一步过程(内存读取,耗时可忽略)。

在这里插入图片描述

这篇关于【数据库】聊聊数据库中的 fetchsize 参数的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/617700

相关文章

Java内存分配与JVM参数详解(推荐)

《Java内存分配与JVM参数详解(推荐)》本文详解JVM内存结构与参数调整,涵盖堆分代、元空间、GC选择及优化策略,帮助开发者提升性能、避免内存泄漏,本文给大家介绍Java内存分配与JVM参数详解,... 目录引言JVM内存结构JVM参数概述堆内存分配年轻代与老年代调整堆内存大小调整年轻代与老年代比例元空

MySQL数据库中ENUM的用法是什么详解

《MySQL数据库中ENUM的用法是什么详解》ENUM是一个字符串对象,用于指定一组预定义的值,并可在创建表时使用,下面:本文主要介绍MySQL数据库中ENUM的用法是什么的相关资料,文中通过代码... 目录mysql 中 ENUM 的用法一、ENUM 的定义与语法二、ENUM 的特点三、ENUM 的用法1

Java中调用数据库存储过程的示例代码

《Java中调用数据库存储过程的示例代码》本文介绍Java通过JDBC调用数据库存储过程的方法,涵盖参数类型、执行步骤及数据库差异,需注意异常处理与资源管理,以优化性能并实现复杂业务逻辑,感兴趣的朋友... 目录一、存储过程概述二、Java调用存储过程的基本javascript步骤三、Java调用存储过程示

Go语言数据库编程GORM 的基本使用详解

《Go语言数据库编程GORM的基本使用详解》GORM是Go语言流行的ORM框架,封装database/sql,支持自动迁移、关联、事务等,提供CRUD、条件查询、钩子函数、日志等功能,简化数据库操作... 目录一、安装与初始化1. 安装 GORM 及数据库驱动2. 建立数据库连接二、定义模型结构体三、自动迁

嵌入式数据库SQLite 3配置使用讲解

《嵌入式数据库SQLite3配置使用讲解》本文强调嵌入式项目中SQLite3数据库的重要性,因其零配置、轻量级、跨平台及事务处理特性,可保障数据溯源与责任明确,详细讲解安装配置、基础语法及SQLit... 目录0、惨痛教训1、SQLite3环境配置(1)、下载安装SQLite库(2)、解压下载的文件(3)、

MySQL数据库的内嵌函数和联合查询实例代码

《MySQL数据库的内嵌函数和联合查询实例代码》联合查询是一种将多个查询结果组合在一起的方法,通常使用UNION、UNIONALL、INTERSECT和EXCEPT关键字,下面:本文主要介绍MyS... 目录一.数据库的内嵌函数1.1聚合函数COUNT([DISTINCT] expr)SUM([DISTIN

MySQL追踪数据库表更新操作来源的全面指南

《MySQL追踪数据库表更新操作来源的全面指南》本文将以一个具体问题为例,如何监测哪个IP来源对数据库表statistics_test进行了UPDATE操作,文内探讨了多种方法,并提供了详细的代码... 目录引言1. 为什么需要监控数据库更新操作2. 方法1:启用数据库审计日志(1)mysql/mariad

postgresql数据库基本操作及命令详解

《postgresql数据库基本操作及命令详解》本文介绍了PostgreSQL数据库的基础操作,包括连接、创建、查看数据库,表的增删改查、索引管理、备份恢复及退出命令,适用于数据库管理和开发实践,感兴... 目录1. 连接 PostgreSQL 数据库2. 创建数据库3. 查看当前数据库4. 查看所有数据库

从入门到精通MySQL 数据库索引(实战案例)

《从入门到精通MySQL数据库索引(实战案例)》索引是数据库的目录,提升查询速度,主要类型包括BTree、Hash、全文、空间索引,需根据场景选择,建议用于高频查询、关联字段、排序等,避免重复率高或... 目录一、索引是什么?能干嘛?核心作用:二、索引的 4 种主要类型(附通俗例子)1. BTree 索引(

Oracle 数据库数据操作如何精通 INSERT, UPDATE, DELETE

《Oracle数据库数据操作如何精通INSERT,UPDATE,DELETE》在Oracle数据库中,对表内数据进行增加、修改和删除操作是通过数据操作语言来完成的,下面给大家介绍Oracle数... 目录思维导图一、插入数据 (INSERT)1.1 插入单行数据,指定所有列的值语法:1.2 插入单行数据,指