数据库流式查询
数据库流式查询是一种边读取边处理数据的查询方式,不会一次性将全量结果加载到内存,能大幅降低内存占用,避免大数据量场景下的OOM问题,同时省去手动分页的复杂逻辑。
一、核心特点
- 内存友好:数据逐行/小批次读取处理,处理完即释放,百万级数据场景下内存峰值可低至几十MB。
- 性能稳定:无需深度分页,仅执行一次查询,避免分页查询的多次IO开销。
- 实时处理:读取数据的同时可同步执行业务逻辑,适配低延迟处理需求。
二、主流实现方式
- MyBatis/MyBatis-Plus 游标实现
通过Cursor迭代器逐条获取数据,需保持数据库连接开启,搭配@Transactional和try-with-resources使用,是Java生态最常用的方案。 - ResultHandler 回调方式
通过@Options注解配置流式参数,查询结果直接回调处理,无需手动管理迭代器,适配简单数据处理场景。 - 云原生流数据库方案
如Azure Databricks结构化流、RisingWave等,支持直接从Kafka、云存储等数据源实时流式读取,适配实时分析、事件驱动场景。
三、典型应用场景
- 百万/千万级大数据量导出、数据迁移
- 实时数据流处理(如金融交易、物联网数据采集)
- 低内存资源环境下的海量数据遍历处理
四、代码示例
java
package com.example.mapper;
import com.example.entity.User;
import org.apache.ibatis.annotations.*;
import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.session.ResultHandler;
import java.util.List;
@Mapper
public interface UserMapper {
/**
* 方式一:使用 Cursor 进行流式查询
* 注意:
* 1. 返回类型必须是 Cursor<T>
* 2. 需要配合 @Options 设置 fetchSize 和 resultSetType
* 3. 调用方必须保持事务开启,并在 try-with-resources 中关闭 Cursor
*/
@Select("SELECT id, username, email, age FROM users")
@Options(resultSetType = ResultSetType.FORWARD_ONLY, fetchSize = 1000)
Cursor<User> selectAllUsersWithCursor();
/**
* 方式二:使用 ResultHandler 进行流式回调处理
* 注意:
* 1. 返回类型必须是 void
* 2. 最后一个参数必须是 ResultHandler<T>
* 3. MyBatis 每读取一行数据就会调用 handler.handleResult
*/
@Select("SELECT id, username, email, age FROM users")
@Options(resultSetType = ResultSetType.FORWARD_ONLY, fetchSize = 1000)
void selectAllUsersWithHandler(ResultHandler<User> handler);
}