导出csv方案优化

举报
Nick Qiu 发表于 2021/07/24 16:41:38 2021/07/24
【摘要】 简介上一篇文章,笔者使用excel的一片入门文章,在本文笔者将会继续上一篇深入研究一下相关优化方案。实验数据,191944行记录,csv文件总大小58m。 执行方案 不优化直接全部读出然后导出:代码package mainimport ( "encoding/csv" "fmt" "log" "os" "time")var tFmt = "2006-01-02 15:04:05"func ...

简介

上一篇文章,笔者使用excel的一篇入门文章,在本文笔者将会继续上一篇深入研究一下相关优化方案。

实验数据,191944行记录,csv文件总大小58m。

执行方案

不优化

直接全部读出然后导出:

  • 代码
package main

import (
	"encoding/csv"
	"fmt"
	"log"
	"os"
	"time"
)

var tFmt = "2006-01-02 15:04:05"

func main() {
	content := readFile()
	fmt.Printf("%s - read file end......%d\n",time.Now().Format(tFmt),len(content))
	exportFile(content)
}

func readFile() [][]string {
	//准备读取文件
	fileName := "RPT_NORMAL_D_共享审批效率.csv"
	fmt.Printf("%s - read file......\n",time.Now().Format(tFmt))
	fs1, _ := os.Open(fileName)
	r1 := csv.NewReader(fs1)
	content, err := r1.ReadAll()
	if err != nil {
		log.Fatalf("can not readall, err is %+v", err)
	}
	return content
}
func exportFile(content [][]string)  {
	fmt.Printf("%s - start export  file......\n",time.Now().Format(tFmt))

	// 1. 导出头到文件1
	f, err := os.Create("test.csv")
	if err != nil {
		panic(err)
	}
	defer f.Close()
	f.WriteString("\xEF\xBB\xBF")// 写入UTF-8 BOM
	w := csv.NewWriter(f)
	//data := content[:1]
	w.WriteAll(content)
	w.Flush()
	fmt.Printf("%s - end export  file......\n",time.Now().Format(tFmt))
}

  • 测试日志,如下可以看出导出时间1秒内可以完成 :
# 数据整包一次导出
2021-07-24 15:30:28 - read file......
2021-07-24 15:30:28 - read file end......191944
2021-07-24 15:30:28 - start export  file......
2021-07-24 15:30:28 - end export  file......

时间优化

开几个线程并发的写文件,然后调用命令合并文件

  • 执行脚本
go run main.go
echo $(date )" - start merge file"
cat temp1.csv temp2.csv temp3.csv  temp4.csv> test.csv
echo $(date )" - end merge file"
  • 执行代码
package main

import (
	"encoding/csv"
	"fmt"
	"log"
	"os"
	"strconv"
	"time"
)

var tFmt = "2006-01-02 15:04:05"

func main() {
	content := readFile()
	fmt.Printf("%s - read file end......%d\n",time.Now().Format(tFmt),len(content))
	exportFile(content)
	time.Sleep(time.Millisecond*200)
}

func readFile() [][]string {
	//准备读取文件
	fileName := "RPT_NORMAL_D_共享审批效率.csv"
	fmt.Printf("%s - read file......\n",time.Now().Format(tFmt))
	fs1, _ := os.Open(fileName)
	r1 := csv.NewReader(fs1)
	content, err := r1.ReadAll()
	if err != nil {
		log.Fatalf("can not readall, err is %+v", err)
	}
	return content
}
func exportFile(content [][]string)  {
	fmt.Printf("%s - start export multiple  file......\n",time.Now().Format(tFmt))
	length := len(content)
	// 1. 导出头到文件1
	f, err := os.Create("test.csv")
	if err != nil {
		panic(err)
	}
	defer f.Close()
	f.WriteString("\xEF\xBB\xBF")// 写入UTF-8 BOM
	w := csv.NewWriter(f)
	data := content[:1]
	w.WriteAll(data)

	// 2. 并发循环导出,一次100,000 (10万数据) 到 1个文件,最终输出n个10万数据的文件
	count := 1
	pos :=1
	for pos < length {
		maxPos := pos+50000
		if maxPos > length {
			maxPos = length
		}
		temp := content[pos: maxPos ]
		go wiriteTempFile(pos,"temp"+strconv.Itoa(count)+".csv",temp)
		count++
		pos += 50000
	}
	// 3. 合并n个文件到文件1
	w.Flush()
	//fmt.Printf("%s - export file success......\n",time.Now().Format(tFmt))
}

func wiriteTempFile(pos int,file string,data [][] string)  {
	fmt.Printf("len === %d , pos=%d \n",len(data),pos)
	f, err := os.Create(file)
	if err != nil {
		panic(err)
	}
	defer f.Close()
	//f.WriteString("\xEF\xBB\xBF") // 写入UTF-8 BOM
	w := csv.NewWriter(f)
	w.WriteAll(data)
	w.Flush()
}
# 启动4个线程并发导出4个文件,然后调用命令合并文件
$ sh merge.sh
2021-07-24 16:30:06 - read file......
2021-07-24 16:30:07 - read file end......191944
2021-07-24 16:30:07 - start export multiple  file......
len === 50000 , pos=1
len === 50000 , pos=50001
len === 41943 , pos=150001
len === 50000 , pos=100001
Sat Jul 24 16:30:07 2021 - start merge file
Sat Jul 24 16:30:08 2021 - end merge file

结论

50m级别,不管是多线程导出还是单线程全部导出处理对时间影响不明显

在实际生产总,可以根据具体情况选择解决方案:

1、源数据一次读出,一次写入文件导出,无限制条件
2、源数据分批读出,分批写入文件,优化内存和CPU,牺牲速度,增对超大文件,不放使用该方案设计离线导出
3、源数据分批多线程读出,分批多线程写入不同文件,合并文件,牺牲内存和CPU,争取速度(文件不大情况下可能时间更慢)

空了尝试一下G级别的文件,使用方法3的效果。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。