本文原文链接
尼恩说在前面
在40岁老架构师 尼恩的读者交流群(50+)中,最近有小伙伴拿到了一线互联网企业如得物、阿里、滴滴、极兔、有赞、希音、百度、网易、美团的面试资格,最近有小伙伴面试招商银行,遇到下面的绝命 12题,狠狠被拷打了, 彻底懵了。 项目场景题太难了,不好好准备,真的答不出!
所以,尼恩给大家做一下系统化、体系化的梳理,使得大家内力猛增,可以充分展示一下大家雄厚的 “技能肌肉”,让面试官爱到 “不能自已、口水直流”,然后实现”offer直提”。
当然,这道面试题,以及参考答案,也会收入咱们的 《尼恩Java面试宝典PDF》V171版本,供后面的小伙伴参考,提升大家的 3高 架构、设计、开发水平。
最新《尼恩 架构条记》《尼恩高并发三部曲》《尼恩Java面试宝典》的PDF,请关注本公众号【技能自由圈】获取,复兴:领电子书
招商银行的Java后端面试真题
被狠狠拷打了,问的人都懵了。 项目场景题太难了,不好好准备,真的答不出!
尼恩将给出上面 招商银行绝命12 题的 全部答案:
1.如何让系统抗住双十一的预约抢购活动?
16大绝招,完成10Wqps秒杀架构(3万字架构长文)
2.如何从零搭建10万级QPS大流量、高并发优惠券系统?
100万用户,抢10万优惠券,如何设计?
3.百万级别数据的 Excel 如何快速导入到数据
就是本文。
4.如何设计一个支持万亿GB网盘实现秒传与限速的系统?
即将发布。
5.如何根据应用场景选择合适的消息中间件?
即将发布。
6.如何提升 RocketMQ 顺序消费性能?
即将发布。
7.使用分布式调度框架该考虑哪些题目?
即将发布。
9.如何让系统抗住双十一的预约抢购活动?
16大绝招,完成10Wqps秒杀架构(3万字架构长文)
10.问 : 如何办理高并发下的库存抢购超卖少买?
即将发布。
11.为什么高并发下数据写入不推荐关系数据?
即将发布。
12.假如让你设计一个分布式链路跟踪系统?
即将发布。
前言
在一样寻常的开发中,用的比较多的方式就是 Apache 下的 POI 框架了,但在现在数据量大的时代下,这种方式 已经不恰当了, 当数据量过大时, POI 框架会出现 OOM 异常,
但是作为数据量小场景下的操作框架,还是OK的。百万级数据量的场景,这个就不可了。
这里,尼恩先是介绍原始 Apache POI ,然后介绍阿里巴巴开源框架,做对比介绍。
POI 框架特性对比
Apache POI 是 Apache 软件基金会的开放源码函式库,用于操作 Microsoft Office 格式文件,如 Excel、Word 和 PowerPoint 等。它提供了一组 Java API,让开发者可以大概在 Java 程序中创建、读取和修改这些文件格式,而无需依赖于 Microsoft Office 软件本身。
poi 依赖的基础接口: WorkBook ,有几种实现子类必要进行区分,如下:
HSSFWorkbook
HSSFWorkbook 重要处理 Excel 的.xls格式文件,Excel 2003(包含) 之前版本使用的子类对象,处理的文件格式都是 .xls 的,其是 poi 中最常用的方式,
HSSFWorkbook 提供了创建工作簿(HSSFWorkbook)、工作表(HSSFSheet)、行(HSSFRow)和单位格(HSSFCell)等对象的功能。
例如,可以使用这些对象来设置单位格的值、样式(如字体、颜色、对齐方式等)。
HSSFWorkbook 处理的行数在 6W+,一样平常处理的数据不凌驾这个大小就不会出现内存溢出的,这个量内存也是充足支撑的.
XSSFWorkbook:
Excel 2003-2007 使用的子类对象,现在还是有大量公司使用的这个,文件格式为 .xlsx,
XSSFWorkbook 用于处理 Excel 的.xlsx格式文件。
XSSFWorkbook 的功能与 HSSF 类似,但由于.xlsx格式是基于 XML 的,在处理大型文件时大概会有更好的性能和功能。例如,XSSF 支持更多的单位格样式和数据验证规则。
XSSFWorkbook 格式就是为了突破 HSSFWorkBook 6W 数据的局限,是为了针对Excel2007版本的 1048576行,16384 列,最多可以导出 104w 条数据,
虽然 XSSFWorkbook在数据上增长了,但是内存的瓶颈也就来了,OOM 离之不远了.
SXSSFWorkbook:
该实现类是 POI3.8 之后的版本才有的, 它可以操作 Excel2007 以后的所有版本 Excel,扩展名是 .xlsx
SXSSFWorkbook 是 XSSFWorkbook 的一个扩展,用于处理非常大的 Excel 文件。
SXSSFWorkbook 通过将数据缓存在内存和磁盘中,避免了一次性将大量数据加载到内存中导致内存溢出的题目,从而可以大概有效地处理大型 Excel 文件。
SXSSFWorkbook方式提供了一种低内存占用机制,存储百万数据丝毫不是题目,一样平常不会出现内存溢出(它使用硬盘来换内存,也就是说当内存数据到达肯定时会接纳硬盘来进行存储,内存里存储的只会是最新的数据),
缺点: SXSSFWorkbook使用到了硬盘,当数据到达硬盘以后,也就无法完成数据的克隆或者公式计算,sheet.clone() 已经无法被支持了
XSSFWorkbook VS SXSSFWorkbook 如何选择
在使用过程中,推荐使用 SXSSFWorkbook 或者 XSSFWorkbook
- 数据量不凌驾 6W~7W 也涉及到了公式的计算,推荐使用 XSSFWorkbook
- 假如不涉及到 Excel 公式和样式, 并且数据量较大的环境下,推荐使用 SXSSFWorkbook ;
POI 在 Excel 中的应用示例
POI 写入 Excel 文件:
下面是一个经典的 Excel 工作簿 写入的案例。
- import org.apache.poi.ss.usermodel.Cell;
- import org.apache.poi.ss.usermodel.Row;
- import org.apache.poi.xssf.usermodel.XSSFSheet;
- import org.apache.poi.xssf.usermodel.XSSFWorkbook;
- import java.io.FileOutputStream;
- import java.io.IOException;
- public class CreateExcel {
- public static void main(String[] args) {
- XSSFWorkbook workbook = new XSSFWorkbook();
- XSSFSheet sheet = workbook.createSheet("Sheet1");
- // 创建行
- Row row = sheet.createRow(0);
- // 创建单元格并设置值
- Cell cell = row.createCell(0);
- cell.setCellValue("Hello, POI!");
- try {
- FileOutputStream outputStream = new FileOutputStream("example.xlsx");
- workbook.write(outputStream);
- workbook.close();
- outputStream.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
复制代码 首先创建一个XSSFWorkbook对象,它代表一个 Excel 工作簿。
然后通过workbook.createSheet方法创建一个工作表。接着在工作表中创建行和单位格,并使用cell.setCellValue方法设置单位格的值。
最后将工作簿写入文件流,天生 Excel 文件。
POI 读取 Excel 文件:
下面是一个经典的 Excel 工作簿 写入的案例。
- import org.apache.poi.ss.usermodel.Cell;
- import org.apache.poi.ss.usermodel.Row;
- import org.apache.poi.xssf.usermodel.XSSFSheet;
- import org.apache.poi.xssf.usermodel.XSSFWorkbook;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.IOException;
- public class ReadExcel {
- public static void main(String[] args) {
- try {
- FileInputStream file = new FileInputStream(new File("example.xlsx"));
- XSSFWorkbook workbook = new XSSFWorkbook(file);
- XSSFSheet sheet = workbook.getSheetAt(0);
- for (Row row : sheet) {
- for (Cell cell : row) {
- switch (cell.getCellType()) {
- case STRING:
- System.out.print(cell.getStringCellValue() + " ");
- break;
- case NUMERIC:
- System.out.print(cell.getNumericCellValue() + " ");
- break;
- // 可以处理其他类型的单元格数据
- }
- }
- System.out.println();
- }
- workbook.close();
- file.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
复制代码 上面的经典代码, 首先通过FileInputStream读取 Excel 文件,然后创建XSSFWorkbook对象。
通过workbook.getSheetAt方法获取工作表,再使用嵌套的循环遍历行和单位格。
根据单位格的数据类型(如字符串、数字等),使用不同的方法获取单位格的值并打印出来。
尼恩给大家画了一下,这个程序的流程图:
POI应用场景和优势
应用场景1 : 数据导出和报表天生:
在企业级应用中,经常必要将数据库中的数据导出为 Excel 或 Word 格式的报表。
POI 可以方便地将数据填充到表格中,设置表格样式和格式,天生专业的报表。
例如,财务系统可以使用 POI 将财务数据天生 Excel 报表,人力资源系统可以使用 POI 天生员工信息的 Word 文档。
应用场景2 : 文件格式转换:
可以将一种 Office 格式转换为另一种格式。
例如,将.doc文件转换为.docx文件,或者将.xls文件转换为.xlsx文件,方便文件的同一管理和共享。
应用场景3 : 小批量 数据 处理:
对于大量的 Office 文件,如必要批量修改文件中的数据、样式或者进行数据提取,POI 可以编写主动化脚本进行处理。
例如,在文档考核流程中,批量提取 Word 文档中的关键信息进行检查。
POI 优势:
- 跨平台:作为 Java 库,POI 可以在任何支持 Java 运行环境的平台上使用,这使得它在企业级的异构系统中非常有用。
- 开源免费:POI 是开源软件,开发者可以免费使用和修改其代码,降低了开发成本。
- 功能丰富:可以大概处理多种 Office 文件格式,并且提供了详细的 API 来操作文件的各个元素,如文档结构、内容、样式等。
POI 的不足:
大数据量 , POI 要么是 OOM,要么借助 磁盘,速率太慢。
百万级数据量办理思绪
使用传统的 poi 导入导出方式,当数据量过大时,明显会出现 OOM 异常,
因此, 尼恩 推荐大家使用阿里巴巴开源的 easyExcel 框架作为导入导出的媒介
GitHub - alibaba/easyexcel: 快速、简单避免OOM的处理Excel工具
EasyExcel 是阿里巴巴开源的一款基于 Java 的简单、省内存的 Excel 处理工具。
EasyExcel 重要办理了 Apache POI 在处理大量数据时大概出现的内存溢出题目,提供了更加便捷、高效的 Excel 读写操作。
EasyExcel 重要优势有两点:
一:内存优化:
EasyExcel 使用了 Sax 剖析模式,在剖析 Excel 文件时接纳一行一行读取的方式,避免了将整个文件加载到内存中,大大淘汰了内存的使用,适用于处理大型 Excel 文件。
二:使用方便:
EasyExcel 提供了简单的 API,使得读取和写入 Excel 数据变得更加容易,开发人员可以通过少量代码实现复杂的 Excel 操作。
EasyExcel的应用示例
导入EasyExcel依赖
在 pom.xml 中添加以下依赖:
- <dependency>
- <groupId>com.alibaba</groupId>
- <artifactId>easyexcel</artifactId>
- <version>3.1.1</version>
- </dependency>
复制代码 使用EasyExcel读取 Excel 文件
以下是一个简单的读取 Excel 文件的示例:
- import com.alibaba.excel.EasyExcel;
- import com.alibaba.excel.context.AnalysisContext;
- import com.alibaba.excel.event.AnalysisEventListener;
- import java.util.ArrayList;
- import java.util.List;
- public class ReadExcel {
- public static void main(String[] args) {
- String fileName = "path/to/your/excel/file.xlsx";
- List<DemoData> demoDataList = new ArrayList<>();
- // 匿名内部类实现监听器
- EasyExcel.read(fileName, DemoData.class, new AnalysisEventListener<DemoData>() {
- @Override
- public void invoke(DemoData data, AnalysisContext context) {
- demoDataList.add(data);
- }
- @Override
- public void doAfterAllAnalysed(AnalysisContext context) {
- // 读取完所有数据后的操作
- System.out.println("Read " + demoDataList.size() + " rows of data.");
- }
- }).sheet().doRead();
- // 打印读取的数据
- for (DemoData data : demoDataList) {
- System.out.println(data);
- }
- }
- }
- @data // getter 和 setter 方法
- class DemoData {
- private String name;
- private Integer age;
- }
复制代码 上面使用EasyExcel.read 方法, 读取文件,核心的参数如下:
- fileName:要读取的 Excel 文件的路径。
- DemoData.class:将 Excel 中的数据映射到 DemoData 类的对象。
- AnalysisEventListener<DemoData>:监听器,用于处理读取到的数据。
- invoke(DemoData data, AnalysisContext context):每读取一行数据,就会调用该方法,将数据添加到 demoDataList 中。
- doAfterAllAnalysed(AnalysisContext context):读取完所有数据后调用该方法。
AnalysisEventListener 接口分析
上面的代码中,非常告急的是 AnalysisEventListener
AnalysisEventListener 是 EasyExcel 中的一个核心接口,用于监听 Excel 文件读取过程中的事件。
通过实现这个接口,可以对读取到的数据进行处理,好比数据转换、数据校验、异常处理等。
AnalysisEventListener 的一些重要功能和用法如下:
- 数据转换与处理方法 invoke(T data, AnalysisContext context):
这是 AnalysisEventListener 中最告急的方法之一,EasyExcel 在剖析每一行数据后会调用此方法。
在这里,你可以对数据进行处理,好比数据转换、数据校验等。
每读取一行数据,invoke 方法就会被调用一次,参数 data 是转换后的 Java 对象,context 提供了分析的上下文信息。
- 使用invoke 实现 批量处理:
在 invoke 方法中, 可以将数据临时存储到一个列表中,当列表达到肯定数量后,可以进行批量处理,好比批量存储到数据库中。如许可以进步数据导入的效率。
- 异常处理:
onException(Exception exception, AnalysisContext context):当读取过程中出现异常时,会调用此方法。在这里可以进行异常处理,好比记录日志、抛出自界说异常等。
- 所有数据读取完毕后的处理 doAfterAllAnalysed:
doAfterAllAnalysed(AnalysisContext context):在所有数据都被分析后,会调用此方法。
可以用于执行一些清理工作,或者处理那些必要在所有数据读取完毕后才能进行的操作,好比批量存储剩余的数据。
- 其他方法:
大家本身去读源码吧。
AnalysisEventListener 监听器是 EasyExcel 处理大数据量 Excel 文件时不可或缺的一部门,它提供了一种流程化的方式来处理数据,使得代码更加简洁和易于维护。
通过实现 AnalysisEventListener,可以机动地处理 Excel 文件中的数据,使得数据导入变得更加可控和高效。
EasyExcel写入 Excel 文件
以下是一个简单的写入 Excel 文件的示例
- import com.alibaba.excel.EasyExcel;
- import java.util.ArrayList;
- import java.util.List;
- public class WriteExcel {
- public static void main(String[] args) {
- String fileName = "path/to/your/output/file.xlsx";
- List<DemoData> demoDataList = new ArrayList<>();
- demoDataList.add(new DemoData("Alice", 25));
- demoDataList.add(new DemoData("Bob", 30));
- EasyExcel.write(fileName, DemoData.class).sheet("Sheet1").doWrite(demoDataList);
- }
- }
复制代码 使用EasyExcel.write(fileName, DemoData.class) 进行写入, 参数介绍如下:
- fileName:要写入的 Excel 文件的路径。
- DemoData.class:要写入的数据对应的类。
- sheet("Sheet1"):指定写入的工作表名称。
- doWrite(demoDataList):将 demoDataList 中的数据写入 Excel 文件。
百万级数据量的高速导入的架构设计
尼恩设计了 高性能 EasyExcel 分片读取 + 高性能Distruptor 队列缓冲 + 高并发 batch批量写入 结合的架构方案,具体如下:
- 高性能分片读取:
针对百万数据读取,选择分片读取,防止出现 OOM 。
这里使用EasyExcel 高性能组件进行分片读取。
- 高性能 队列缓冲:
百万数据的数据,必要用一个队列聚集缓存起来,以方便做一些必要的业务处理如校验,也方便很后面的的批量写入。
- 高并发批量写入:
选择batch批写的方式 , 实现百万数据的写入,这里使用Mybatis-plus的分批插入,并且结合接纳多线程处理。
交互图:数据导入、队列缓冲和 写入模块 三者之间的交互图
以下是一个完备的交互图,展示了上述架构方案的交互数据流,包含数据导入模块、高并发队列缓冲和数据写入模块。
百万级数据量的高速导入的代码实现
以下是一个完备的实现上述架构方案的示例代码,包含数据导入模块、高并发队列缓冲和数据写入模块。
使用 Spring Boot 和 MyBatis-Plus 框架,并结合 EasyExcel 进行数据读取和 MyBatis-Plus 进行数据写入,同时使用 Disruptor 作为高并发队列缓冲:
1. 引入依赖
首先,在 pom.xml 文件中添加所需的依赖:
- <dependencies>
- <!-- Spring Boot Starter Web -->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-web</artifactId>
- <version>2.5.6</version>
- </dependency>
- <!-- EasyExcel 依赖 -->
- <dependency>
- <groupId>com.alibaba</groupId>
- <artifactId>easyexcel</artifactId>
- <version>3.1.1</version>
- </dependency>
- <!-- MyBatis-Plus 依赖 -->
- <dependency>
- <groupId>com.baomidou</groupId>
- <artifactId>mybatis-plus-boot-starter</artifactId>
- <version>3.4.3.4</version>
- </dependency>
- <!-- Disruptor 依赖 -->
- <dependency>
- <groupId>com.lmax</groupId>
- <artifactId>disruptor</artifactId>
- <version>3.4.4</version>
- </dependency>
- <!-- MySQL 驱动 -->
- <dependency>
- <groupId>mysql</groupId>
- <artifactId>mysql-connector-java</artifactId>
- <version>8.0.26</version>
- </dependency>
- <!-- Lombok 依赖,用于简化实体类代码 -->
- <dependency>
- <groupId>org.projectlombok</groupId>
- <artifactId>lombok</artifactId>
- <version>1.18.22</version>
- <scope>provided</scope>
- </dependency>
- </dependencies>
复制代码 2. 配置 Spring Boot 应用程序
创建一个 Spring Boot 主应用程序类:
- import org.springframework.boot.SpringApplication;
- import org.springframework.boot.autoconfigure.SpringBootApplication;
- import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
- import org.mybatis.spring.annotation.MapperScan;
- @SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
- @MapperScan("com.example.demo.mapper")
- public class ExcelImportDemoApplication {
- public static void main(String[] args) {
- SpringApplication.run(ExcelImportDemoApplication.class, args);
- }
- }
复制代码 3. 创建实体类
创建一个与数据库表对应的实体类 DataRecord:
- import com.baomidou.mybatisplus.annotation.TableId;
- import com.baomidou.mybatisplus.annotation.TableName;
- import lombok.Data;
- @Data
- @TableName("data_record")
- public class DataRecord {
- @TableId
- private Long id;
- private String column1;
- private String column2;
- private String column3;
- }
复制代码 4. 创建控制器类
DataImportController 类, 这个非常简单,核心就是下面的方法:
importData(@RequestParam("file") MultipartFile file) 方法处理文件上传,调用 DataImportService 进行数据导入。
- import com.example.demo.service.DataImportService;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.web.bind.annotation.PostMapping;
- import org.springframework.web.bind.annotation.RequestParam;
- import org.springframework.web.bind.annotation.RestController;
- import org.springframework.web.multipart.MultipartFile;
- import java.io.IOException;
- @RestController
- public class DataImportController {
- // 注入数据导入服务
- @Autowired
- private DataImportService dataImportService;
- // 处理文件上传和数据导入的接口
- @PostMapping("/import")
- public String importData(@RequestParam("file") MultipartFile file) throws IOException {
- // 调用数据导入服务进行数据导入
- dataImportService.importData(file.getInputStream());
- return "数据导入成功";
- }
- }
复制代码 5. 高性能分片读取 数据 服务类 DataImportService
高性能分片读取 数据 服务类 DataImportService 的处理流程的简单介绍:
DataImportService 首先会吸取一个文件的输入流,这个输入流是要导入的数据的来源,通常可以是用户上传的 Excel 文件或其他数据源。
为 EasyExcel 创建一个名为 DataRecordExcelListener 的监听器。
这个监听器的作用是在 EasyExcel 读取数据的过程中处理数据的读取事件。
调用 EasyExcel 的 read 方法开始读取文件,使用创建的 DataRecordExcelListener 来监听数据读取的过程。
在读取过程中,会逐行读取文件中的数据。
每读取一行数据,将其添加到一个批处理列表中。
当批处理列表中的数据量达到 10000 条时:
- 将该批处理列表的数据发布到 DataRecordDisruptor 中,以便后续处理。
- 清空批处理列表,为存储下一批数据做好准备。
- 当文件中没有更多的数据必要读取时,会检查批处理列表是否还有未处理的数据。
- 假如批处理列表不为空(即还有未达到 10000 条的数据),将其发布到 DataRecordDisruptor` 中。
总体而言,DataImportService 利用 EasyExcel 逐行读取文件数据,将数据按批处理列表存储,达到肯定数量后将数据发送到 DataRecordDisruptor 进行后续的处理。
这个过程通过批处理和使用 DataRecordDisruptor 实现了高性能的分片读取和数据缓冲,避免了大量数据读取时大概出现的内存溢出题目,并进步了数据处理的性能和效率。
高性能分片读取 数据 服务类 DataImportService 流程图如下

高性能分片读取 数据 服务类 DataImportService 参考代码 如下
- import com.alibaba.excel.EasyExcel;
- import com.alibaba.excel.context.AnalysisContext;
- import com.alibaba.excel.event.AnalysisEventListener;
- import com.example.demo.disruptor.DataRecordDisruptor;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Service;
- import java.io.InputStream;
- import java.util.ArrayList;
- import java.util.List;
- @Service
- public class DataImportService {
- // 注入 Disruptor 组件
- @Autowired
- private DataRecordDisruptor dataRecordDisruptor;
- // 数据导入的主要方法,接收文件输入流
- public void importData(InputStream inputStream) {
- // 使用 EasyExcel 进行分片读取,添加自定义的监听器
- EasyExcel.read(inputStream, DataRecord.class, new DataRecordExcelListener(dataRecordDisruptor)).sheet().doRead();
- }
- // EasyExcel 的监听器,用于处理读取到的数据
- public static class DataRecordExcelListener extends AnalysisEventListener<DataRecord> {
- private final DataRecordDisruptor disruptor;
- // 存储数据的批处理列表
- private final List<DataRecord> batch = new ArrayList<>();
- public DataRecordExcelListener(DataRecordDisruptor disruptor) {
- this.disruptor = disruptor;
- }
- @Override
- public void invoke(DataRecord data, AnalysisContext context) {
- // 将读取到的数据添加到批处理列表中
- batch.add(data);
- // 当达到批处理大小,将数据发布到 Disruptor 进行处理
- if (batch.size() >= 10000) {
- disruptor.publish(batch);
- batch.clear();
- }
- }
- @Override
- public void doAfterAllAnalysed(AnalysisContext context) {
- // 处理最后一批数据,确保所有数据都被处理
- if (!batch.isEmpty()) {
- disruptor.publish(batch);
- }
- }
- }
- }
复制代码 DataImportService 类importData(InputStream inputStream) 方法:
使用 EasyExcel 进行分片读取,使用自界说的 DataRecordExcelListener 监听器处理读取到的数据。
DataRecordExcelListener是一个内部类,两个方法如下:
第一个方法 invoke(DataRecord data, AnalysisContext context)
这个方法将读取的数据添加到 batch 列表,当 batch 大小达到 10000 时,将数据发布到 DataRecordDisruptor。
第二个方法doAfterAllAnalysed(AnalysisContext context) 方法
这个方法确保最后一批数据也能被处理。
5. 高性能Distruptor 队列缓冲 Disruptor 无锁队列
这里设计了一个DataRecordDisruptor 类,在整个数据导入架构中扮演着告急的脚色,它作为高并发队列缓冲:
- 一方面利用 Disruptor 的高性能特性缓存和缓冲数据,
- 另一方面将数据以合适的批处理大小进行批量存储,淘汰了数据库的操作次数,进步了团体的数据处理效率。
DataRecordDisruptor结合了 Disruptor 的高性能和 MyBatis-Plus 的批量插入功能,为处理大量数据提供了一种高效的机制, 核心的流程如下:
首先,初始化 Disruptor:首先会设置 Disruptor 的环形缓冲区大小、事件工厂、线程工厂、生产者类型和等待策略。
环形缓冲区大小决定了可以存储多少数据事件,这里设置的大小可根据实际需求调整。
事件工厂用于创建 DataRecordEvent 对象,线程工厂负责创建处理数据的线程,生产者类型设置为多生产者模式,以支持多个来源的数据,等待策略则是 BlockingWaitStrategy,它会在缓冲区满时阻塞生产者,防止数据丢失。
然后,启动 Disruptor:完成上述设置后,启动 Disruptor,使其处于可吸取数据的状态。
等待吸取数据:启动后,DataRecordDisruptor 处于等待吸取数据的状态,它将吸取来自 DataImportService 的数据。
存储数据到环形缓冲区:当吸取到来自 DataImportService 的数据时,将这些数据存储在环形缓冲区中。环形缓冲区是 Disruptor 的核心组件,它提供了高效的数据存储和访问机制。
- 数据处理事件触发:当数据存储到环形缓冲区时,会触发相应的数据处理事件。
- 添加数据到批处理列表:将触发的数据添加到一个批处理列表中。这个批处理列表用于临时存储数据,方便后续的批量操作。
- 判断是否达到批处理条件:检查批处理列表的大小是否达到或凌驾 1000 条数据,或者是否处理完一批数据。这个批处理大小是为了优化数据库操作,淘汰数据库交互次数。
- 批量插入操作:假如满足上述条件,使用 MyBatis-Plus 进行批量插入操作,将数据存储到数据库中。
- 清空批处理列表:完成批量插入后,清空批处理列表,为下一批数据的存储和处理做好准备。
- 只要还必要吸取数据,整个过程会不绝重复上述步骤,持续进行数据的吸取、存储、处理和插入操作,直到没有更多的数据必要处理。
DataRecordDisruptor参考代码 如下
7. 高并发 batch批量写入 的 Mapper 接口
数据写入模块包括:
- DataRecordMapper 接口:
自界说 insertBatch(List<DataRecord> dataRecords) 方法,在 XML 映射文件中实现批量插入逻辑。
- DataRecordMapper.xml:
使用 MyBatis-Plus 的 <foreach> 标签实现批量插入。
创建一个 MyBatis-Plus 的 Mapper 接口:
- import com.baomidou.mybatisplus.core.mapper.BaseMapper;
- import com.example.demo.entity.DataRecord;
- @Mapper
- public interface DataRecordMapper extends BaseMapper<DataRecord> {
- // 自定义的批量插入方法
- void insertBatch(List<DataRecord> dataRecords);
- }
复制代码 8. 实现 MyBatis-Plus 批量插入(XML)
在 resources/mapper/DataRecordMapper.xml 中添加以下代码:
- <!DOCTYPE mapper
- PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
- "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
- <mapper namespace="com.example.demo.mapper.DataRecordMapper">
- <insert id="insertBatch">
- insert into data_record (column1, column2, column3) values
- <foreach collection="list" item="item" separator=",">
- (#{item.column1}, #{item.column2}, #{item.column3})
- </foreach>
- </insert>
- </mapper>
复制代码 也可以使用手动提交事务 + preparestatement,进行批量插入。
具体的实现代码,这里忽略。
后面介绍尼恩Java面试宝典配套视频的时间,会共同视频进行介绍。
9. 配置文件
在 application.properties 中配置数据库毗连:
- spring.datasource.url=jdbc:mysql://localhost:3306/your_database_name?useSSL=false&serverTimezone=UTC
- spring.datasource.username=root
- spring.datasource.password=your_password
- spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
复制代码 百万级数据量的高速导入的总结
尼恩设计了 高性能 EasyExcel 分片读取 + 高性能Distruptor 队列缓冲 + 高并发 batch批量写入 结合的架构方案,具体如下:
- 这个示例实现了一个完备的数据导入架构,使用 EasyExcel 进行高性能分片读取,避免了内存溢出题目。
- 使用 Disruptor 作为高并发队列缓冲,将数据存储在环形缓冲区中,方便进行后续的业务处理。
- 使用 MyBatis-Plus 的批量插入功能,并结合多线程处理,实现了高并发的批量数据写入。
通过以上架构和代码,你可以实现百万级数据量的快速导入,利用各组件的优势进步系统的性能和可扩展性。
性能可以由原来的500秒优化到20秒!
说在最后:有题目找老架构取经
回到开始的时间的面试题:招商银行的Java后端面试真题
被狠狠拷打了,问的人都懵了。 项目场景题太难了,不好好准备,真的答不出!
按照此文的套路去回答,肯定会 吊打面试官,让面试官爱到 “不能自已、口水直流”,然后实现”offer直提”。
在面试之前,建议大家系统化的刷一波 5000页《尼恩Java面试宝典PDF》,里边有大量的大厂真题、面试难题、架构难题。
很多小伙伴刷完后, 吊打面试官, 大厂横着走。
在刷题过程中,假如有啥题目,大家可以来 找 40岁老架构师尼恩交流。
另外,假如没有面试机会,可以找尼恩来改简历、做帮扶。前段时间,刚指导一个小伙 暴涨200%(2倍),29岁/7年/双非一本 , 从13K 涨到 37K ,逆天改命。
狠狠卷,实现 “offer自由” 很容易的, 前段时间一个武汉的跟着尼恩卷了2年的小伙伴, 在非常严寒/痛楚被裁的环境下, offer拿到手软, 实现真正的 “offer自由” 。
尼恩技能圣经系列PDF
- 《NIO圣经:一次穿透NIO、Selector、Epoll底层原理》
- 《Docker圣经:大白话说Docker底层原理,6W字实现Docker自由》
- 《K8S学习圣经:大白话说K8S底层原理,14W字实现K8S自由》
- 《SpringCloud Alibaba 学习圣经,10万字实现SpringCloud 自由》
- 《大数据HBase学习圣经:一本书实现HBase学习自由》
- 《大数据Flink学习圣经:一本书实现大数据Flink自由》
- 《响应式圣经:10W字,实现Spring响应式编程自由》
- 《Go学习圣经:Go语言实现高并发CRUD业务开发》
……完备版尼恩技能圣经PDF集群,请找尼恩领取
《尼恩 架构条记》《尼恩高并发三部曲》《尼恩Java面试宝典》PDF,请到下面公号【技能自由圈】取↓↓↓
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |