golang 小工具,导入csv到Mysql

一个导数据的小工具,很久没写golang了,现学现卖,一开始写了版同步版本的,12万数据处理了2个小时,然后写了版协程的,5分钟处理完了,随手记录一下。

这里要备注一下,表的where条件加索引,也是性能提升的关键点,在你的task运行缓慢的时候,不要只顾着检查程序逻辑,还要考虑外部因素。

应该还有优化的空间。

 

还可以扩展一下,如果我们有12W 条数据,要对其中一列长度为 20位数据进行去重,怎么才能高效筛选出重复?

打算后续再实践一下。

往常都有布隆过滤器,bitmap,都是处理整型的,那么这个字符串,我们要如何将它hash成对应的位置呢?

这个hash 算法是否会出现冲突呢?冲突概率多大呢?性能损耗又有多少?这里应该得到的是准确计算还是求可能会重复的近似集合能将性能发挥到最佳又可以最贴近我们的结果呢?广告之后,马上回来。

 

package main

import (
	"bufio"
	"database/sql"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"io"
	"log"
	"os"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"time"
)

const (
	DB = "test"
	MYSQL_HOST = "localhost"
	MYSQL_USER = "root"
	MYSQL_PWD = "123456"
	IMPORT_FILE_PATH = "215.csv"
	LOG_FILE = "import.log"
)

var (
	mysqld * sql.DB
	err error
	fileHandler,logFileHandler * os.File
	fileLogger * log.Logger
)

func init(){
	//限制使用1个CPU线程,顺序执行.sync 版本
	runtime.GOMAXPROCS(4)

	//链接mysql
	mysqlDSN := fmt.Sprintf("%s:%s@tcp(%s:3306)/%s?charset=utf8",MYSQL_USER,MYSQL_PWD,MYSQL_HOST,DB)
	mysqld,err = sql.Open("mysql",mysqlDSN)

	if err != nil && mysqld.Ping() != nil {
		fmt.Println("Mysql Error")
		panic(err)
	}

	mysqld.SetMaxOpenConns(30)
	mysqld.SetMaxIdleConns(10)
	mysqld.SetConnMaxLifetime(time.Minute*120)

	//打开文件
	fileHandler,err = os.Open(IMPORT_FILE_PATH)

	if err != nil{
		fmt.Println("Import File Open Error")
		panic(err)
	}

	logFileHandler,err = os.OpenFile(LOG_FILE,  os.O_CREATE|os.O_WRONLY|os.O_APPEND,0666)

	if err != nil {
		fmt.Println("Logger File Open Error")
		panic(err)
	}

	fileLogger = log.New(io.MultiWriter(logFileHandler),"",log.LstdFlags)
}


func main(){

	var syncW = &sync.WaitGroup{}

	//读取文件
	bufRead := bufio.NewReader(fileHandler)
	i := 0;

	defer fileHandler.Close()
	defer logFileHandler.Close()
	defer mysqld.Close()

	startDateTIme := time.Now().Format("2006-01-02 15:22:11")

	fileLogger.Println("Process Start:"+startDateTIme)

	for{

		line,fileErr := bufRead.ReadString('\n')

		if fileErr != nil && fileErr != io.EOF {
			fmt.Println("导入文件读取错误,错误行数:"+ strconv.Itoa(i))
			panic(fileErr)
		}

		if fileErr == io.EOF {
			fmt.Println("文件处理完毕,总共处理数据:"+strconv.Itoa(i)+"条")
			break
		}

		line = strings.Trim(line,"\n")
		//永远无法自动退出
		syncW.Add(1)

		if i == 0{
			i++
			fmt.Println(line)
			fmt.Println("数据处理中。。")
			continue
		}

		i++



		go pushToMysql(line,mysqld,fileLogger,syncW)

	}

	syncW.Wait()

}


func pushToMysql(line string,mysqld *sql.DB,fileLogger * log.Logger,syncW * sync.WaitGroup){

	defer syncW.Done()

	fileLogger.Println("[Data]"+line)
	data := strings.Split(line,",")

	if len(data) != 4 {
		fileLogger.Println("[DataError][DataFormatError]"+line)
		return
	}

	formatTime,err:=time.Parse("2006-01-02",data[2])
	payTime := formatTime.Unix()
	formatTime,err = time.Parse("2006-01-02",data[3])
	payEndTIme := formatTime.Unix()

	if err != nil {
		fileLogger.Println("[DataError][DateFormatError]"+line)
		return
	}

	createdAt := time.Now().Unix()            //单位s,打印结果:1491888244
	var countRecord int
	checkSql := fmt.Sprintf("SELECT count(id) FROM `abc` WHERE `a`='%s' ",data[0])

	err = mysqld.QueryRow(checkSql).Scan(&countRecord)

	if err != nil {
		fileLogger.Println("[DataError][checkDataError]"+err.Error())
		return
	}

	if countRecord > 0 {
		fileLogger.Println("[DataError][DateRepeat]"+line)
		return
	}

	sql := fmt.Sprintf("insert into abc(`a`,`b`,`c`,`d`,`e`,`f`) value('%s','%s',%d,%d,%d,%d)",
		data[0],
		data[1],
		99,
		payTime,
		payEndTIme,
		createdAt)

	result,exeErr := mysqld.Exec(sql)

	if exeErr != nil {
		fileLogger.Println("[DataError][SqlError]"+exeErr.Error())
		return
	}

	id,insertErr := result.LastInsertId();

	if insertErr != nil {
		fileLogger.Println("[DataError][InsertError]"+insertErr.Error())
		return
	}

	fileLogger.Println("[Success]"+line+",[DataId]"+strconv.FormatInt(id,10))
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值