MySQL 单库的并发写入是有性能瓶颈的,一般情况5K TPS写入就很高了 。
当前数据都采用 SSD 存储,性能应该更好一些 。但如果是HDD的话,虽然顺序读写会有非常高的表现 , 但HDD无法应对并发写入,例如每个库10张表,假设10张表在并发写入,每张表虽然是顺序写入,由于多个表的存储位置不同,HDD只有1个磁头,不支持并发写 , 只能重新寻道,耗时将大大增加 , 失去顺序读写的高性能 。
所以对于HDD而言,单库并发写多个表并不是好的方案 。回到SSD的场景,不同SSD厂商的写入能力不同,对于并发写入的能力也不同 , 有的支持500M/s,有的支持1G/s读写,有的支持8个并发,有的支持4个并发 。在线上实验之前,我们并不知道实际的性能表现如何 。
所以在设计上要更加灵活,需要支持以下能力:
- 支持配置数据库的数量
- 支持配置并发写表的数量(如果MySQL是HDD磁盘,只让一张表顺序写入,其他任务等待)
接下来聊一下文件读取 , 10亿条数据,每条1K,一共是931G 。近1T大文件 , 一般不会生成如此大的文件 。所以我们默认文件已经被大致切分为100个文件 。每个文件数量大致相同即可 。
为什么切割为100个呢?切分为1000个,增大读取并发,不是可以更快导入数据库吗?刚才提到数据库的读写性能受限于磁盘,但任何磁盘相比写操作 , 读操作都要更快 。尤其是读取时只需要从文件读取,但写入时MySQL要执行建立索引 , 解析SQL、事务等等复杂的流程 。所以写的并发度最大是100,读文件的并发度无需超过100 。
更重要的是读文件并发度等于分表数量 , 有利于简化模型设计 。即100个读取任务,100个写入任务,对应100张表 。
五、如何保证写入数据库有序
既然文件被切分为100个10G的小文件 , 可以按照文件后缀+ 在文件行号 作为记录的唯一键,同时保证同一个文件的内容被写入同一个表 。例如:
- index_90.txt 被写入 数据库database_9,table_0,
- index_67.txt 被写入 数据库 database_6,table_7 。
六、如何更快地读取文件
10G的文件显然不能一次性读取到内存中,场景的文件读取包括:
- Files.readAllBytes一次性加载内存
- FileReader+ BufferedReader 逐行读取
- File+ BufferedReader
- Scanner逐行读取
- JAVA NIO FileChannel缓冲区方式读取

文章插图
详细的评测内容请参考:读取文件性能比较 (zhuanlan.zhihu.com/p/142029812)
由此可见,使用JavaNIO FileChannnel明显更优 , 但是FileChannel的方式是先读取固定大小缓冲区 , 不支持按行读取 。也无法保证缓冲区正好包括整数行数据 。如果缓冲区最后一个字节正好卡在一行数据中间,还需要额外配合读取下一批数据 。如何把缓冲区变为一行行数据 , 比较困难 。
File file = new File("/xxx.zip");
FileInputStream fileInputStream = null;
long now = System.currentTimeMillis();
try {
fileInputStream = new FileInputStream(file);
FileChannel fileChannel = fileInputStream.getChannel();
int capacity = 1 * 1024 * 1024;//1M
ByteBuffer byteBuffer = ByteBuffer.allocate(capacity);
StringBuffer buffer = new StringBuffer();
int size = 0;
while (fileChannel.read(byteBuffer) != -1) {
//读取后,将位置置为0,将limit置为容量, 以备下次读入到字节缓冲中,从0开始存储
byteBuffer.clear();
byte[] bytes = byteBuffer.array();
size += bytes.length;
}
System.out.println("file size:" + size);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
//TODO close资源.
}
System.out.println("Time:" + (System.currentTimeMillis() - now));
JavaNIO 是基于缓冲区的,ByteBuffer可转为byte数组,需要转为字符串 , 并且要处理按行截断 。
推荐阅读
- 搜索引擎如何判定网站的价值
- 揭秘CSRF攻击:如何防范,增强网站安全性
- TikTok全托管模式是什么?如何解读新模式
- 抖音话题头像怎么设置?抖音话题简介描述介绍如何修改?
- TikTok如何选品?打造爆款不是梦
- 如何发视频赚钱呢?手把手教你做
- 紫砂壶泡茶味道如何
- 如何做麻油鸡,放冰箱的麻油鸡加热要先切块吗??
- 能如何看电脑配置,如何查看自己电脑的配置信息
- 电脑扩展屏幕能如何操作,电脑桌面只在屏幕中间的位置显示怎么办
