10亿数据如何最快插入MySQL?

这是笔者曾经的面试题 , 这个问题并不是要你回答准确的时间,而是考察如何设计一个系统,最快速地插入10亿条数据 。
笔者当时傻乎乎地回答三小时,支支吾吾没说出所以然 。面试官看我没睡醒 , 让我回去等通知 。好在他把简历退给我了,我省了一份简历 。今天尝试重新思考下,好好打他脸 。
最快的速度把10亿条数据导入到数据库 , 首先需要和面试官明确一下,10亿条数据什么形式存在哪里,每条数据多大,是否有序导入,是否不能重复,数据库是否是MySQL?
假设和面试官明确后,有如下约束:

  • 10亿条数据,每条数据 1 Kb
  • 数据内容是非结构化的用户访问日志,需要解析后写入到数据库
  • 数据存放在Hdfs 或 S3 分布式文件存储里
  • 10亿条数据并不是1个大文件,而是被近似切分为100个文件,后缀标记顺序
  • 要求有序导入,尽量不重复
  • 数据库是 MySQL
一、数据库单表能支持10亿吗?
首先考虑10亿数据写到MySQL单表可行吗?
答案是不能,单表推荐的值是2000W以下 。这个值怎么计算出来的呢?
MySQL索引数据结构是B+树,全量数据存储在主键索引,也就是聚簇索引的叶子结点上 。B+树插入和查询的性能和B+树层数直接相关,2000W以下是3层索引,而2000w以上则可能为四层索引 。
Mysql b+索引的叶子节点每页大小16K 。当前每条数据正好1K,所以简单理解为每个叶子节点存储16条数据 。b+索引每个非叶子节点大小也是16K,但是其只需要存储主键和指向叶子节点的指针,我们假设主键的类型是 BigInt,长度为 8 字节 , 而指针大小在 InnoDB 中设置为 6 字节,这样一共 14 字节,这样一个非叶子节点可以存储 16 * 1024/14=1170 。
也就是每个非叶子节点可关联1170个叶子节点,每个叶子节点存储16条数据 。由此可得到B+树索引层数和存储数量的表格 。2KW 以上 索引层数为 4 层,性能更差 。
10亿数据如何最快插入MySQL?

文章插图
更详细的请参考B+树层数计算(bAIjiahao.baidu.com/s?id=1709205029044038742&wfr=spider&for=pc) 。
为了便于计算,我们可以设计单表容量在1KW,10亿条数据共100个表 。
二、如何高效写入数据库
单条写入数据库性能比较差,可以考虑批量写入数据库 , 批量数值动态可调整 。每条1K,默认可先调整为100条批量写入 。
批量数据如何保证数据同时写成功?MySQL Innodb存储引擎保证批量写入事务同时成功或失败 。
【10亿数据如何最快插入MySQL?】写库时要支持重试,写库失败重试写入,如果重试N次后依然失败 , 可考虑单条写入100条到数据库,失败数据打印记录,丢弃即可 。
此外写入时按照主键id顺序顺序写入可以达到最快的性能,而非主键索引的插入则不一定是顺序的,频繁的索引结构调整会导致插入性能下降 。最好不创建非主键索引,或者在表创建完成后再创建索引,以保证最快的插入性能 。
是否需要并发写同一个表
不能 。
  • 并发写同一个表无法保证数据写入时是有序的 。
  • 提高批量插入的阈值,在一定程度上增加了插入并发度,无需再并发写入单表 。
三、MySQL存储引擎的选择
Myisam 比innodb有更好的插入性能,但失去了事务支持,批量插入时无法保证同时成功或失败,所以当批量插入超时或失败时,如果重试,势必导致一些重复数据的发生 。但是为了保证更快的导入速度,可以把myisam存储引擎列为计划之一 。
现阶段我引用一下别人的性能测试结果 # MyISAM与InnoDB对比分析 (t.csdn.cn/eFm9z) 。
10亿数据如何最快插入MySQL?

文章插图
从数据可以看到批量写入明显优于单条写入 。并且在innodb关闭即时刷新磁盘策略后,innodb插入性能没有比myisam差太多 。
innodb_flush_log_at_trx_commit:控制MySQL刷新数据到磁盘的策略 。
  • 默认=1 , 即每次事务提交都会刷新数据到磁盘,安全性最高不会丢失数据 。
  • 当配置为0、2 会每隔1s刷新数据到磁盘 ,  在系统宕机、mysql crash时可能丢失1s的数据 。
考虑到Innodb在关闭即时刷新磁盘策略时,批量性能也不错,所以暂定先使用innodb(如果公司MySQL集群不允许改变这个策略值,可能要使用MyIsam了) 。线上环境测试时可以重点对比两者的插入性能 。
四、要不要进行分库


推荐阅读