10亿数据高效插入MySQL最佳方案

作者:微信小助手

发布时间:2024-01-16T08:44:12



写在文章开头

你好,我叫sharkchili,目前还是在一线奋斗的Java开发,经历过很多有意思的项目,也写过很多有意思的文章,是CSDN Java领域的博客专家,也是Java Guide的维护者之一,非常欢迎你关注我的公众号:「写代码的SharkChili」,这里面会有笔者精心挑选的并发、JVM、MySQL数据库专栏,也有笔者日常分享的硬核技术小文。

问题简介

我们直接引入本文要探讨的问题,现在要求用最快的速度把10亿条左右的数据存到数据库中,对应的该实现有着以下几个要求:

  1. 每条数据转为数据库数据大约 1k
  2. 数据都存在 txt文档中,需要进行解析后才能存到数据库中。
  3. 所有数据对应的文档都存在 HdfsS3分布式文件存储里。
  4. 数据被分到100个文件中,通过文件的后缀确定这些数据的批次。
  5. 要求保证有序导入,数据尽可能不重复。
  6. 存储的数据库是 MySQL

分析问题点

针对需求整理分析,我们对该功能提出以下几个问题点:

  1. 如何插入?是批量插入还是逐条插入?
  2. 这种数据量是否需要考虑一下分库分表?
  3. 插入时是用串行插入数据表还是并行插入数据表?
  4. MySQL存储引擎如何选择?
  5. 如何实现快速读取数据?
  6. 如何保证的写入时有序,即按照100个文件对应的数据顺序进行存储?
  7. 我们希望将这个插入功能以任务为单位进行,要如何封装?
  8. 如何实现分布式节点并发工作?

逐个击破

如何插入?批量插入还是单条插入?

考虑到按照主键id顺序自增顺序写入可以达到最快性能,所以我们的数据表的主键id一定是顺序自增的,其次我们都知道MySQL插入操作中最耗时的操作是网络链接,所以我们采用的插入方式是批量插入。为了避免每次进行批量插入时,JDBC需要预编译SQL发起的网络IO,我们使用Mybatis进行插入时,会采用ExecutorType.BATCH的方式进行批处理插入,代码示例大抵如下所示:

@Autowired
    private SqlSessionFactory sqlSessionFactory;

/**
     * session插入
     */

    @Test
    void batchInsert() {
       //获取一个BATCH的sqlSession 
        SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
        BatchInsertTestMapper sqlSessionMapper = sqlSession.getMapper(BatchInsertTestMapper.class);

        long start = System.currentTimeMillis();

        for (BatchInsertTest batchInsertTest : testList) {
           //生成插入语句
            sqlSessionMapper.insert(batchInsertTest);
        }
        //使用批处理的方式一次性提交这批插入SQL
        sqlSession.commit();
        long end = System.currentTimeMillis();
        log.info("批处理插入