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))
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章