(转)快速安全的往mysql里插入千万条数据

2023-11-08

来源:ksfzhaohui
juejin.im/post/5da5b568f265da5b6c4bc13d

概述

有个需求解析一个订单文件了,并且说明文件可达到千万条数据,每条数据大概在20哥字段左右,每个字段使用逗号分隔,需要尽量在半小时内入库
思路

1.估算文件大小

因为告诉文件有千万条,同时每条记录大概在20哥字段左右,所以可用大致估算一下整个订单文件的大小,方法也很简单使用FileWriter往文件中插入一千万条数据,查看文件大小,经测试大概在1.5G左右;

2.如何批量插入

由上可知文件比较大,一次性读取内存肯定不行,方法是每次从当前订单文件中截取一部分数据,然后进行批量插入,如何批次插入可以使用insert((…)values(…),(…)的方式,经测试这种方式效率还是挺高的;

3.数据的完整性

截取数据的时候需要注意,需要保证数据的完整性,每条记录最后都是一个换行符,需要根据这个标识保证每次截取都是整条数,不要出现半条数据这种情况。

4.数据库是否支持批次数据

因为需要进行批次数据的插入,数据库是否支持大量数据写入,比如使用的是mysql,可以通过设置max_allowed_packet来保证批次提交的数据量;

5.中途出错的情况

因为是大文件解析,如果中途出现错误,比如数据刚好插入到900w的时候,数据库连接失败,这种情况不可能重新来插一遍,所有需要记录每次插入数据的位置,并且需要保证和批次的数据在同一个事务中,这样恢复之后可以从记录的位置开始继续插入。

实现

1.准备数据表

这里需要准备两张表分别是:订单状态位置信息表,订单表;

CREATE TABLE `file_analysis` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `file_type` varchar(255) NOT NULL COMMENT '文件类型 01:类型1,02:类型2',
  `file_name` varchar(255) NOT NULL COMMENT '文件名称',
  `file_path` varchar(255) NOT NULL COMMENT '文件路径',
  `status` varchar(255) NOT NULL COMMENT '文件状态  0初始化;1成功;2失败:3处理中',
  `position` bigint(20) NOT NULL COMMENT '上一次处理完成的位置',
  `crt_time` datetime NOT NULL COMMENT '创建时间',
  `upd_time` datetime NOT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
CREATE TABLE `file_order` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `file_id` bigint(20) DEFAULT NULL,
  `field1` varchar(255) DEFAULT NULL,
  `field2` varchar(255) DEFAULT NULL,
  `field3` varchar(255) DEFAULT NULL,
  `field4` varchar(255) DEFAULT NULL,
  `field5` varchar(255) DEFAULT NULL,
  `field6` varchar(255) DEFAULT NULL,
  `field7` varchar(255) DEFAULT NULL,
  `field8` varchar(255) DEFAULT NULL,
  `field9` varchar(255) DEFAULT NULL,
  `field10` varchar(255) DEFAULT NULL,
  `field11` varchar(255) DEFAULT NULL,
  `field12` varchar(255) DEFAULT NULL,
  `field13` varchar(255) DEFAULT NULL,
  `field14` varchar(255) DEFAULT NULL,
  `field15` varchar(255) DEFAULT NULL,
  `field16` varchar(255) DEFAULT NULL,
  `field17` varchar(255) DEFAULT NULL,
  `field18` varchar(255) DEFAULT NULL,
  `crt_time` datetime NOT NULL COMMENT '创建时间',
  `upd_time` datetime NOT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10000024 DEFAULT CHARSET=utf8

2.配置数据库包大小

mysql> show VARIABLES like '%max_allowed_packet%';
+--------------------------+------------+
| Variable_name            | Value      |
+--------------------------+------------+
| max_allowed_packet       | 1048576    |
| slave_max_allowed_packet | 1073741824 |
+--------------------------+------------+
2 rows in set

mysql> set global max_allowed_packet = 1024*1024*10;
Query OK, 0 rows affected

通过设置max_allowed_packet,保证数据库能够接收批次插入的数据包大小;不然会出现如下错误

Caused by: com.mysql.jdbc.PacketTooBigException: Packet for query is too large (4980577 > 1048576). You can change this value on the server by setting the max_allowed_packet' variable.
    at com.mysql.jdbc.MysqlIO.send(MysqlIO.java:3915)
    at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2598)
    at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2778)
    at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2834)

3.准备测试数据

public static void main(String[] args) throws IOException {
    FileWriter out = new FileWriter(new File("D://xxxxxxx//orders.txt"));
    for (int i = 0; i < 10000000; i++) {
        out.write(
                "vaule1,vaule2,vaule3,vaule4,vaule5,vaule6,vaule7,vaule8,vaule9,vaule10,vaule11,vaule12,vaule13,vaule14,vaule15,vaule16,vaule17,vaule18");
        out.write(System.getProperty("line.separator"));
    }
    out.close();
}

使用FileWriter遍历往一个文件里插入1000w条数据即可,这个速度还是很快的,不要忘了在每条数据的后面添加换行符(\n\r);

4.截取数据的完整性

除了需要设置每次读取文件的大小,同时还需要设置一个参数,用来每次获取一小部分数据,从这小部分数据中获取换行符(\n\r),如果获取不到一直累加直接获取为止,这个值设置大小大致同每条数据的大小差不多合适,部分实现如下:

ByteBuffer byteBuffer = ByteBuffer.allocate(buffSize); // 申请一个缓存区
long endPosition = batchFileSize + startPosition - buffSize;// 子文件结束位置

long startTime, endTime;
for (int i = 0; i < count; i++) {
    startTime = System.currentTimeMillis();
    if (i + 1 != count) {
        int read = inputChannel.read(byteBuffer, endPosition);// 读取数据
        readW: while (read != -1) {
            byteBuffer.flip();// 切换读模式
            byte[] array = byteBuffer.array();
            for (int j = 0; j < array.length; j++) {
                byte b = array[j];
                if (b == 10 || b == 13) { // 判断\n\r
                    endPosition += j;
                    break readW;
                }
            }
            endPosition += buffSize;
            byteBuffer.clear(); // 重置缓存块指针
            read = inputChannel.read(byteBuffer, endPosition);
        }
    } else {
        endPosition = fileSize; // 最后一个文件直接指向文件末尾
    }
    ...省略,更多可以查看Github完整代码...
}

如上代码所示开辟了一个缓冲区,根据每行数据大小来定大概在200字节左右,然后通过遍历查找换行符(\n\r),找到以后将当前的位置加到之前的结束位置上,保证了数据的完整性;

5.批次插入数据

通过insert(…)values(…),(…)的方式批次插入数据,部分代码如下:

// 保存订单和解析位置保证在一个事务中
SqlSession session = sqlSessionFactory.openSession();
try {
    long startTime = System.currentTimeMillis();
    FielAnalysisMapper fielAnalysisMapper = session.getMapper(FielAnalysisMapper.class);
    FileOrderMapper fileOrderMapper = session.getMapper(FileOrderMapper.class);
    fileOrderMapper.batchInsert(orderList);

    // 更新上次解析到的位置,同时指定更新时间
    fileAnalysis.setPosition(endPosition + 1);
    fileAnalysis.setStatus("3");
    fileAnalysis.setUpdTime(new Date());
    fielAnalysisMapper.updateFileAnalysis(fileAnalysis);
    session.commit();
    long endTime = System.currentTimeMillis();
    System.out.println("===插入数据花费:" + (endTime - startTime) + "ms===");
} catch (Exception e) {
    session.rollback();
} finally {
    session.close();
}
...省略,更多可以查看Github完整代码...

如上代码在一个事务中同时保存批次订单数据和文件解析位置信息,batchInsert通过使用mybatis的``标签来遍历订单列表,生成values数据;

总结

以上展示了部分代码,完整的代码可以查看Github地址中的batchInsert模块,本地设置每次截取的文件大小为2M,经测试1000w条数据(大小1.5G左右)插入mysql数据库中,大概花费时间在20分钟左右,当然可以通过设置截取的文件大小,花费的时间也会相应的改变。

完整代码

https://github.com/ksfzhaohui/blog/tree/master/mybatis

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

(转)快速安全的往mysql里插入千万条数据 的相关文章

  • 在 Java 中捕获(捕获)窗口中的鼠标光标

    我正在寻找一种方法 在鼠标进入窗口后捕获或捕获该窗口中的鼠标 就像鼠标被捕获在虚拟机窗口中一样 直到用户按 CTRL ALT DEL 或以其他方式释放鼠标 我如何在 Java 中实现这一点 全屏显示不是一个选择 EDIT 这里有一些 SSC
  • 不支持的字段:将瞬间格式化为日期 ISO 时的年份[重复]

    这个问题在这里已经有答案了 我正在尝试将 Instant 格式化为 ldap 日期 ISO8601 但在 f format Instant now 处失败 String input 20161012235959 0Z DateTimeFor
  • 如何在ArrayList中的特定位置插入对象

    假设我有一个大小为 n 的对象的 ArrayList 现在我想在特定位置插入另一个对象 假设在索引位置 k 大于 0 且小于 n 并且我希望索引位置 k 处及其之后的其他对象向前移动一个索引位置 那么有没有什么方法可以直接在Java中做到这
  • 将处理项目移至 Eclipse

    我已经在处理项目上工作了一段时间 现在想将其移至 Eclipse 中 我已经在 Eclipse 环境中安装了 Proclipse 我有很多扩展名为 pde 的文件 然而 Proclipse 文件都以 java 结尾 所有 pde 文件都存在
  • 如何在 Android 中的 Chrome 或 Firefox 等特定浏览器的 Web 视图中加载应用程序

    我是 Android 新手 我正在做一个应用程序 我需要在平板电脑上的 Web 视图中加载现有的应用程序 在平板电脑中 当我使用 Web 视图加载应用程序时 我的应用程序将加载到默认浏览器中 如何在平板电脑上的 Web 视图中的特定浏览器
  • java 中的梵文 i18n

    我正在尝试使用来自互联网的示例 ttf 文件在 java 中使用 i18n 进行梵文 印地文 我可以加载资源包条目 还可以加载 ttf 并设置字体 但它不会根据需要呈现 jlabel 它显示块代替字符 如果我在 Eclipse 中调试 我可
  • 如何从 Java 访问 Windows 设备管理器中的信息?

    我有一个串行 USB 设备 并且其中多个设备可以连接到计算机 我需要查询和检索设备连接到的 COM 端口列表 在 Windows 设备管理器中 您可以获得当前连接的设备的 COM 端口 友好名称 该列表是动态的 从注册表中读取不工作 htt
  • JAXB 继承冲突 - 重新注释子类

    目前我的项目中有这样的环境 public abstract class Foo private List
  • H2数据库:如何进行加密保护,而不暴露文件加密密钥

    我们在服务器模式下使用Java H2数据库 因为我们不希望用户访问数据库文件 为了对数据库文件添加更多保护 我们计划使用 AES 加密 将 CIPHER AES 添加到数据库 URL 以防存储被盗 但是 每个用户在连接时还需要提供文件保护密
  • 使用 kryo 注册课程的策略

    我最近发现了 kryonet 库 它非常棒并且非常适合我的需求 然而 我遇到的一个问题是制定一种好的策略来注册所有可以转移的类 我知道我可以在每个对象中编写一个静态方法 该方法将返回它使用的所有类的列表 但我真的不想这样做 为了我自己的时间
  • 对对象集合进行排序[重复]

    这个问题在这里已经有答案了 如果我有一个简单的字符串列表 List
  • 在拇指上方显示修改后的 JSlider 值

    有没有一种简单的方法可以在使用某些 外观和感觉 的同时更改 JSlider 上方标签中显示的值 为了清楚起见 我正在谈论这个值 具体来说 我想显示除以 1000 的值而不是值本身 我知道如果我显示它们 我可以为刻度设置标签 但用户将不得不猜
  • 膨胀类 android.support.design.widget.NavigationView 时出错

    我按照 NavigationView 的教程进行操作 但无法解决此错误消息 Error inflating class android support design widget NavigationView 教程链接 https www
  • RxJava android mvp 单元测试 NullPointerException

    我是 mvp 单元测试的新手 我想对演示者进行一个非常基本的测试 它负责登录 我只想断言 view onLoginSuccess 这是演示者代码 public LoginPresenter LoginViewContract loginVi
  • ActiveMQ JNDI 查找问题

    尝试使用 JNDI 运行以下 ActiveMQ http activemq apache org jndi support html http ActiveMQ 20JNDI 并且我的 jboss server node lib 文件夹中有
  • 错误膨胀类 android.support.design.widget.NavigationView [启动时崩溃]

    该应用程序应该有一个导航抽屉 可以从左侧拉出并显示各种活动 但是一旦将导航栏添加到 XML Activity homescreen 文档中 应用程序一启动就会崩溃 主屏幕 java package com t99sdevelopment c
  • 了解Kafka流groupBy和window

    我无法理解 kafka 流中的 groupBy groupById 和窗口的概念 我的目标是聚合一段时间内 例如 5 秒 的流数据 我的流数据看起来像 value 0 time 1533875665509 value 10 time 153
  • 如何在 spring-data 中强制使用 CrudRepository 进行预加载?

    我有一个实体 其中包含List就是这样lazy默认加载 interface MyEntityRepository extends CrudRepository
  • 从字节数组设置 img src

    我需要设置img src我在对象中拥有的字节数组的属性 img
  • 如何在J2ME中获取数字的幂[重复]

    这个问题在这里已经有答案了 可能的重复 J2ME power double double 数学函数实现 https stackoverflow com questions 2076913 j2me powerdouble double ma

随机推荐

  • SpringSecurity学习笔记(二)加载的Filter,默认配置

    参考视频 编程不良人 SpringSecurity中的过滤器 以上图片来自官网 Security过滤器通过SecurityFilterChain API插入FilterChainProxy 过滤器的顺序很重要 通常不需要知道Spring S
  • ChatGPT对于留学生带来了哪些影响?留学生又应该如何使用?

    悉尼大学的赶DUE高峰期来得总是那么早 才开学四周 图书馆就已经被赶DUE人占领地满满当当 面对即将到来的大批量写作任务 不少同学们打起了ChatGPT的主意 2022年11月 人工智能 AI 工具ChatGPT Chat Generati
  • IDEA 创建web项目

    IntelliJ IDEA 13 新版本发布 第一时间去官网看了下 黑色的主题 很给力 大体使用了下 对于一开始就是用eclipse的童鞋们 估计很难从eclipse中走出来 当然 我也很艰难的走在路上 首先要说一点 在IntelliJ I
  • 不如来试试看这个AI大模型 感觉速度飞快,真的还挺不错呢!

    个人主页 极客小俊 作者简介 web开发者 设计师 技术分享博主 希望大家多多支持一下 我们一起进步 如果文章对你有帮助的话 欢迎评论 点赞 收藏 加关注 介绍 它是国产新一代AI智能模型 拥有跨领域的知识和语言理解能力 能够基于自然对话方
  • SpringBoot热部署 新版idea找不到Onupdate action

    改动到了这里
  • .metadata是什么项目文件_部署springboot+vue项目文档

    一 部署linux nginx 1 部署后台代码 1 1由于本人采用的是idea工具开发 所以在terminal中mvn clean package生成对应的jar包 此jar包可去对应文件所在目录的target查找 1 2将jar复制到l
  • Java中死锁问题如何排查

    问题描述 在java程序中碰到运行未停止 如何证明是死循环还是死锁问题呢 可以使用Java中自带的命令jps l 用来查看当前运行的进程号 在使用jstack 进程号命令查看当前进程的堆栈信息 死锁代码 class DeadLockRes
  • 二进制安装K8S--etcd

    大致步骤 1 准备lunix 虚拟机两台 一台用来部署etcd apiserver 管理组建 另外一台部署worker 节点 2 操作系统初始化 3 为etcd 和APIserver 准备自签名证书 4 部署etcd 集群 5 部署mast
  • 青橙项目问题Cannot access defaults field of Properties和A child container failed during start

    项目场景 在学习黑马青橙电商项目时 用到了代码生成器 生成完代码后 再往下做的时候就出现了问题 Cannot access defaults field of Properties和A child container failed duri
  • JMeter压力测试 5分钟让你学会如何并发压测接口

    文章目录 地址 下载 启动 使用 地址 JMeter官网下载 https jmeter apache org download jmeter cgi 下载 最新款的jmeter需要java8 的支持 请自行安装jdk8或以上的版本 根据系统
  • Hive sql数据倾斜及性能优化

    一 数据倾斜问题剖析 数据倾斜是分布式系统不可避免的问题 任何分布式系统都有几率发生数据倾斜 当然数据倾斜问题是在亿级数据造成机器无法应付这么多数据 这时发生数据倾斜 最后很难算出结果 hive的原理机制是MR 在MR中最容易出现数据倾斜的
  • 《拖延心理学》

    一 拖延处理技巧汇编 1 确立一个可操作性的目标 可观察 具体而实在的 而不是那种模糊而抽象的目标 不是 我要停止拖延 而是 我要在9月1日之前打扫和整理我的车库 2 设定一个务实的目标 不要异想天开 而要从小事做起 不要过于理想化 而是要
  • mysql mariadb 创建mysql mariadb用户,登录报错

    mysql mariadb 创建energy普通用户 登录mysql报错 access denied GRANT REPLICATION CLIENT PROCESS SELECT ON TO mysqld exporter identif
  • 图像超分算法

    图像超分算法旨在将低分辨率 LR 图像转换为高分辨率 HR 图像 例如将480p分辨率的视频转换为1080p分辨率的视频 常见的图像超分算法包括插值法 基于统计的算法和基于学习的算法 1 插值法 最简单的图像超分方法是插值法 它将LR图像中
  • 【mybatis】mybatis访问报错:org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)...

    mybatis访问报错 org apache ibatis binding BindingException Invalid bound statement not found 需要检查的步骤 1 是否mapper java文件上使用了注解
  • L298N芯片驱动电机

    L298N芯片驱动电机 一 控制原理 L298N可以控制两个电机 具体原理为IN1 IN2 IN3 IN4四个输入端口接收控制器发出的电信号 两个输出端分别控制两组直流电机转动 输入端的逻辑控制表如下 GPIO GPIO 0 GPIO 1
  • dos命令之远程桌面

    echo off 开启服务 net start SessionEnv net start TermService 开机手动启动 sc config SessionEnv start demand sc config TermService
  • Qt的getOpenFileName()函数使用

    Qt的getOpenFileName 函数使用 为了说明QFileDialog getOpenFileName 函数的用法 首先要清楚函数的完整定义 QString QFileDialog getOpenFileName QWidget p
  • 2012淘宝校园招聘笔试

    川大站笔试 本人亲手经历 1个小时的笔试时间 人还是非常多 快没有位置了 选择题考察的面非常广 我记得住的只有下面几道题 1 进程死锁 2 双链表插入节点 3 插座问题 用树的知识解决 4 平衡树的概念 红黑树 B数 AVL树 5 ICMP
  • (转)快速安全的往mysql里插入千万条数据

    来源 ksfzhaohui juejin im post 5da5b568f265da5b6c4bc13d 概述 有个需求解析一个订单文件了 并且说明文件可达到千万条数据 每条数据大概在20哥字段左右 每个字段使用逗号分隔 需要尽量在半小时