通常的数据同步中,如果数据量比较少的话可以直接全量同步,默认情况下,完整的检索结果集会将其存储在内存中。在大多数情况下,这是最有效的操作方式,并且由于 MySQL 网络协议的设计,因此更易于实现。但是如果数据量很大的话,全量同步需要大量的内存,如果内存不足的话则可能会导致内存溢出。
通常的会采用分页的方式,一批一批的同步,大体的实现方式如下:
(资料图)
``
```javaint page = 1;int pageNum = 1000;while (true){ UserQueryRequest request = new UserQueryRequest(); request.setPage(page); request.setPageSize(pageNum); PageInfo
这种实现方式虽然可以实现分批同步,但是同步的数据必须先提供实现分页的查询方式,如果数据源是通过复杂的连表查询来的,先实现一个分页查询更是会增加实现的复杂度。解决这个问题可以使用一种更为优雅的解决方式,即使用流失查询。
流式查询,会建立长连接,利用服务端游标,每次读取一条加载到 JVM 内存,因此不会导致内存溢出。
## MyBatis 如何使用流式查询:
### 配置mapper.xml文件:
```xml```
### 自定义一个ResultHandler:
User是自定义的同步对象的实体对象,需要自己定义
```javaimport lombok.extern.slf4j.Slf4j;import model.User;import org.apache.ibatis.session.ResultContext;import org.apache.ibatis.session.ResultHandler;import java.util.ArrayList;import java.util.List;
/** * @author: jie * @create: 2023/3/29 16:51 * @description: */@Slf4jpublic class SyncDataHandler implements ResultHandler
/** * 每批处理数量 */ private final static int BATCH_SIZE = 1000;
/** * 缓存数据 */ private List
/** * 同步熟虑 */ private int total = 0;
@Override public void handleResult(ResultContext extends User> resultContext) { User coreInfoCyDTO = resultContext.getResultObject(); this.cacheList.add(coreInfoCyDTO); //每到达BATCH_SIZE 条数据处理一次 if (this.cacheList.size() >= BATCH_SIZE) { this.handle(); } total++; }
/** * 处理缓存数据 */ private void handle() { try { // 具体的处理逻辑 省略 } finally { // 清除处理过的缓存数据 this.cacheList.clear(); } }
/** * 处理最后一批没有进行处理的数据 */ public int end() { this.end(); return total; }}```
### 使用代码示例:
```javaSyncDataHandler syncDataHandler = new SyncDataHandler();userMapper.getUserList("selectUsers", syncDataHandler);syncDataHandler.end();```
## **结言**
流式查询可以避免 OOM,,数据量大可以考虑此方案,其占用内存大小取决于批处理大小**BATCH_SIZE**的设置。所以**BATCH_SIZE**应该根据业务情况设置合适的大小。但是这这种方式会占用数据库连接,使用中不会释放,所以线上针对大数据量业务用到流式操作,一定要进行并发控制。
标签: