一、普通方式返回100万条数据
@RestController
@RequestMapping("/bad")
public class BadController {@Autowiredprivate UserRepository userRepository;/*** 危险!一次性加载 100 万条到内存*/@GetMapping("/all-users")public List<User> getAllUsers() {// 问题1:把100万条数据全查出来,加载到JVM堆内存List<User> users = userRepository.findAll(); // 问题2:序列化成JSON时还要再占一份内存return users; // 结果:可能占用 500MB~1GB 内存,多个请求就OOM!}
}
问题分析
1.内存爆炸(OutOfMemoryError)
2.接口超时(30s+)
3.数据库压力大
4.客户端卡死
二、流式查询:逐条处理(推荐用于导出、批处理)
核心思想:从数据库读取时不把所有数据加载到内存,而是"一条一条"地读取和处理。
流式查询(如 MyBatis + JDBC 流式)本质上是阻塞式的,但它可以避免内存溢出(OOM),却仍然可能遇到连接或请求超时。
使用 MyBatis 流式查询
// Mapper 接口
@Mapper
public interface UserMapper {@Select("SELECT id, name, age, email FROM user ORDER BY id")void streamAllUsers(ResultHandler<User> handler);
}
// Controller
@RestController
@RequestMapping("/streaming")
public class StreamingController {@Autowiredprivate UserMapper userMapper;/*** 流式返回:逐条写入响应流,内存占用极低*/@GetMapping(value = "/users.csv", produces = "text/csv;charset=UTF-8")public void streamUsersAsCsv(HttpServletResponse response) throws IOException {response.setContentType("text/csv");response.setCharacterEncoding("UTF-8");response.setHeader("Content-Disposition", "attachment; filename=users.csv");PrintWriter writer = response.getWriter();// 写入CSV头writer.println("id,name,age,email");// 流式处理:每读到一条,就写入一次userMapper.streamAllUsers(resultContext -> {User user = resultContext.getResultObject();writer.printf("%d,%s,%d,%s%n", user.getId(), escapeCsv(user.getName()), user.getAge(), user.getEmail());writer.flush(); // 强制推送});}private String escapeCsv(String value) {if (value == null) return "";return value.replace("\"", "\"\"");}
}
优点
1.内存只保留当前一条数据
2.使用 Transfer-Encoding: chunked 分块传输
3.适合导出 CSV、Excel 等大文件
三、响应式流:Reactive Stream(推荐用于高并发 API)
技术栈:Spring WebFlux、Spring Data R2DBC(响应式数据库驱动)、Project Reactor
// Repository
@Repository
public interface UserReactiveRepository extends ReactiveCrudRepository<User, Long> {Flux<User> findAll(); // 返回响应式流
}
// Service
@Service
public class UserReactiveService {@Autowiredprivate UserReactiveRepository userRepository;public Flux<User> getAllUsers() {return userRepository.findAll();}
}
// Controller
@RestController
@RequestMapping("/reactive")
public class ReactiveController {@Autowiredprivate UserReactiveService userService;/*** ✅ 响应式流:非阻塞、背压控制、持续推送*/@GetMapping(value = "/users", produces = MediaType.TEXT_EVENT_STREAM_VALUE)public Flux<User> streamUsers() {// 返回 Flux<User>,Spring WebFlux 会自动流式推送return userService.getAllUsers();// 客户端会一条一条收到数据}
}
配置 application.yml
spring:r2dbc:url: r2dbc:postgresql://localhost:5432/mydbusername: userpassword: pass
前端
// 使用 EventSource(SSE)
const eventSource = new EventSource('/reactive/users');eventSource.onmessage = (event) => {const user = JSON.parse(event.data);console.log('收到用户:', user);// 可以实时显示在页面上
};eventSource.onerror = () => {eventSource.close();
};
优点
1.非阻塞 I/O,高并发
2.背压(Backpressure)机制,客户端慢?服务端自动减速
3.实时性好,适合“直播式”数据推送