前言
- 工作中有个需求,需要导入几个g大小的txt文件到mysql数据库中,并能按条件实时搜索。
解决方案
- mysql数据库分库分表
- tddl分库分表
- 大txt文件拆分成小文件
- 采用命令
split -b
- 第一个参数:目标文件大小,切割成的小文件一个多大。示例是800M,但是m要小写,如果是G则写g。
- 第二个参数:被切割文件路径。
- 第三个参数:目标文件名称。
注:拆分完之后各个文件首尾的数据可能会被分割,需要手动确认修改下。
- 接口导入txt文件
拆分成的小文件可以压缩到一个压缩包里。
- 解压获取压缩包里的所有文件
List<File> fileList = Lists.newArrayList();
File file = new File(multipartFile.getOriginalFilename());
FileUtils.copyInputStreamToFile(multipartFile.getInputStream(), file);
ZipFile zipFile = new ZipFile(file, UTF_8);
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
InputStream inputStream = zipFile.getInputStream(entry);
if (!entry.getName().startsWith(UNDERSCORE)) {
File singleFile = new File(entry.getName());
FileUtils.copyInputStreamToFile(inputStream, singleFile);
fileList.add(singleFile);
}
}
file.delete();
- 多线程批量导入
// 防止线上报错,循环同步执行各个文件
for (File file : fileList) {
List<Callable<Boolean>> callables = Lists.newArrayList();
if (file != null) {
try (Stream<String> stream = Files.lines(Paths.get(file.getPath()))) {
int batchCount = (int) Math.ceil((double) stream.count() / BATCH_SIZE);
for (int page = 1; page <= batchCount; page++) {
try (Stream<String> newStream = Files.lines(Paths.get(file.getPath()))) {
List<DncListDTO> dncListDTOList = newStream.skip((page - 1) * BATCH_SIZE).limit(BATCH_SIZE).map(phone -> {
DncListDTO dncListDTO = DncListConverter.INSTANCE.toDTO(dncListUploadDTO);
return dncListDTO;
}).collect(Collectors.toList());
Callable<Boolean> callable = () -> dncListManager.addList(dncListDTOList);
callables.add(callable);
}
}
}
file.delete();
ThreadPoolUtil.executeTasks(callables, ThreadPoolUtil.DEFAULT_WAIT_SECONDS);
}
}
采用Stream<String> stream = Files.lines(Paths.get(file.getPath()))
方式把文件数据转成stream流,防止OOM
- 同步数据到openSearch或者es