最快的速度把10亿条数据导入到数据库,首先需要和面试官明确一下,10亿条数据什么形式存在哪里,每条数据多大,是否有序导入,是否不能重复,数据库是否是MySQL?
假设和面试官明确后,有如下约束
10亿条数据,每条数据 1 Kb 数据内容是非结构化的用户访问日志,需要解析后写入到数据库 数据存放在 Hdfs
或S3
分布式文件存储里10亿条数据并不是1个大文件,而是被近似切分为100个文件,后缀标记顺序 要求有序导入,尽量不重复 数据库是 MySQL
首先考虑10亿数据写到MySQL单表可行吗?
数据库单表能支持10亿吗?
答案是不能,单表推荐的值是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 层,性能更差。
层数 | 最大数据量 |
---|---|
2 | 1170 * 16 = 18720 |
3 | 1170 * 1170 * 16= 21902400 = 2000w |
4 | 1170 * 1170 * 1170 * 16 = 25625808000 = 256亿 |
为了便于计算,我们可以设计单表容量在1KW,10亿条数据共100个表。
如何高效的写入数据库
单条写入数据库性能比较差,可以考虑批量写入数据库,批量数值动态可调整。每条1K,默认可先调整为100条批量写入。
批量数据如何保证数据同时写成功?MySQL Innodb存储引擎保证批量写入事务同时成功或失败。
写库时要支持重试,写库失败重试写入,如果重试N次后依然失败,可考虑单条写入100条到数据库,失败数据打印记录,丢弃即可。
此外写入时按照主键id顺序顺序写入可以达到最快的性能,而非主键索引的插入则不一定是顺序的,频繁地索引结构调整会导致插入性能下降。最好不创建非主键索引,或者在表创建完成后再创建索引,以保证最快的插入性能。
是否需要并发写同一个表
不能
并发写同一个表无法保证数据写入时是有序的。 提高批量插入的阈值,在一定程度上增加了插入并发度。无需再并发写入单表
MySQL存储引擎的选择
Myisam
比innodb
有更好的插入性能,但失去了事务支持,批量插入时无法保证同时成功或失败,所以当批量插入超时或失败时,如果重试,势必对导致一些重复数据的发生。但是为了保证更快的导入速度,可以把myisam存储引擎列为计划之一。
现阶段我引用一下别人的性能测试结果:MyISAM与InnoDB对比分析
但是使用信号量限流有个问题,如果任务忘记释放信号量,或者进程Crash无法释放信号量,如何处理呢?可以考虑给信号量增加一个超时时间。那么如果任务执行过长,导致提前释放信号量,另一个客户单争抢到信号量,导致 两个客户端同时写一个任务如何处理呢?
what,明明是将10亿数据导入数据库,怎么变成分布式锁超时的类似问题?
实际上 Redisson
的信号量并没有很好的办法解决信号量超时问题,正常思维:如果任务执行过长,导致信号量被释放,解决这个问题只需要续约就可以了,任务在执行中,只要发现快信号量过期了,就续约一段时间,始终保持信号量不过期。但是 Redission并没有提供信号量续约的能力,怎么办?
不妨换个思路,我们一直在尝试让多个节点争抢信号量,进而限制并发度。可以试试选取一个主节点,通过主节点轮训任务表。分三种情况,
情况1 当前执行中数量小于并发度。
则选取id最小的待执行任务,状态置为进行中,通知发布消息。 消费到消息的进程,申请分布式锁,开始处理任务。处理完成释放锁。借助于Redission分布式锁续约,保证任务完成前,锁不会超时。
情况2 当前执行中数量等于并发度。
主节点尝试 get 进行中任务是否有锁。 如果没有锁,说明有任务执行失败,此时应该重新发布任务。如果有锁,说明有任务正在执行中。
情况3 当前执行中数量大于并发度
上报异常情况,报警,人工介入
使用主节点轮训任务,可以减少任务的争抢,通过kafka发布消息,接收到消息的进程处理任务。为了保证更多的节点参与消费,可以考虑增加Kafka分片数。虽然每个节点可能同时处理多个任务,但是不会影响性能,因为性能瓶颈在数据库。
那么主节点应该如何选取呢?可以通过Zookeeper+curator
选取主节点。可靠性比较高。
10亿条数据插入数据库的时间影响因素非常多。包括数据库磁盘类型、性能。数据库分库数量如果能切分1000个库当然性能更快,要根据线上实际情况决策分库和分表数量,这极大程度决定了写入的速率。最后数据库批量插入的阈值也不是一成不变的,需要不断测试调整,以求得最佳的性能。可以按照100,1000,10000等不断尝试批量插入的最佳阈值。
最后总结一下几点重要的
总结
要首先确认约束条件,才能设计方案。确定面试官主要想问的方向,例如1T文件如何切割为小文件,虽是难点,然而可能不是面试官想考察的问题。 从数据规模看,需要分库分表,大致确定分表的规模。 从单库的写入瓶颈分析,判断需要进行分库。 考虑到磁盘对并发写的支持力度不同,同一个库多个表写入的并发需要限制。并且支持动态调整,方便在线上环境调试出最优值。 MySQL innodb、myisam
存储引擎对写入性能支持不同,也要在线上对比验证数据库批量插入的最佳阈值需要反复测试得出。 由于存在并发度限制,所以基于Kafka分离读取任务和写入任务比较困难。所以合并读取任务和写入任务。 需要Redis记录任务执行的进度。任务失败后,重新导入时,记录进度,可避免数据重复问题。 分布式任务的协调工作是难点,使用Redission信号量无法解决超时续约问题。可以由主节点分配任务+分布式锁保证任务排他写入。主节点使用 Zookeeper+Curator
选取。
点击关注,记得星标🌟哦~
【版權聲明】
本文爲轉帖,原文鏈接如下,如有侵權,請聯繫我們,我們會及時刪除
原文鏈接:https://mp.weixin.qq.com/s/TtCf1VzMzPivhLR5-wiBtg Tag: 网站技术 Mysql