一个导数据的小工具,很久没写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))
}