当前位置: 首页 > 科技观察

使用Go语言,25秒读取一个16GB的文件_0

时间:2023-03-19 01:15:21 科技观察

当今世界上任何一个计算机系统,每天都会产生大量的日志或数据。随着系统的发展,将调试数据存储到数据库中是不可行的,因为它们是不可变的,只能用于分析和故障排除。所以大多数公司倾向于将日志存储在文件中,而这些文件通常位于本地磁盘上。我们将使用Go语言从一个大小为16GB的.txt或.log文件中提取日志。让我们开始编码……首先,我们打开文件。对于任何文件IO,我们将使用标准的Goos.File。f,err:=os.Open(fileName)iferr!=nil{fmt.Println("cannotabletoreadthefile",err)return}//UPDATE:closeaftercheckingerrordeferfile.Close()//打开文件后不要忘记关闭文件,我们有以下两种选择可选:逐行读取文件,这有助于减少内存压力,但需要更多时间。一次将整个文件读入内存并处理该文件,这会消耗更多内存,但花费的时间要少得多。由于文件太大,即16GB,因此无法将整个文件加载到内存中。但是第一个选项对我们来说也不可行,因为我们希望在几秒钟内处理文件。但是你猜怎么着,还有第三种选择。瞧...不是将整个文件加载到内存中,在Go中我们还可以使用bufio.NewReader()以块的形式加载文件。r:=bufio.NewReader(f)for{buf:=make([]byte,4*1024)//chunksize,err:=r.Read(buf)//loadingchunktobufferbuf=buf[:n]ifn==0{iferr!=nil{fmt.Println(err)break}iferr==io.EOF{break}returnerr}}一旦我们分块了文件,我们就可以派生一个线程,即Go例程,同时处理多个文件时间块。上面的代码将修改为://syncpoolstoreusethememoryanddecreasethepreassureonGarbageCollectorlinesPool:=sync.Pool{New:func()interface{}{lines:=make([]byte,500*1024)returnlines}}stringPool:=sync.Pool{New:func()interface{}{lines:=""returnlines}}slicePool:=sync.Pool{New:func()interface{}{lines:=make([]string,100)returnlines}}r:=bufio.NewReader(f)varwgsync.WaitGroup//waitgrouptokeeptrackoffallthreadsfor{buf:=linesPool.Get().([]byte)n,err:=r.Read(buf)bufbuf=buf[:n]ifn==0{iferr!=nil{fmt.Println(err)break}iferr==io.EOF{break}returnerr}nextUntillNewline,err:=r.ReadBytes('\n')//readentirelineiferr!=io.EOF{buf=append(buf,nextUntillNewline...)}wg.Add(1)gofunc(){//processeachchunkconcurrently//start->logstarttime,end->logendtimeProcessChunk(buf,&linesPool,&stringPool,&slicePool,start,end)wg.Done()}()}wg.Wait()}上面的代码引入了两个优化点:sync.Pool是一个强大的对象池,可以重用对象来减轻垃圾收集器的压力。我们将跨分片重用内存以减少内存消耗并大大加快我们的工作。GoRoutines帮助我们并发处理缓冲区块,这大大提高了处理速度。现在让我们实现ProcessChunk函数,它将处理以下格式的日志行。2020-01-31T20:12:38.1234Z,SomeField,OtherField,Andsoon,Tillnewline,...\n我们将根据命令行提供的时间戳提取日志。funcProcessChunk(chunk[]byte,linesPool*sync.Pool,stringPool*sync.Pool,slicePool*sync.Pool,starttime.Time,endtime.Time){//anotherwaitgrouptoprocesseverychunkfurthervarwg2sync.WaitGrouplogs:=stringPool.Get().(string)logs=string(chunk)linesPool.Put(chunk)//putbackthechunkinpool//splitthestringby"\n",sothatwehavesliceoflogslogsSlice:=strings.Split(logs,"\n")stringPool.Put(logs)//putbackthestringpoolchunkSize:=100//processthebunchof100logsinthreadn:=len(logsSlice)noOfThread:=n/chunkSizeifn%chunkSize!=0{//checkforoverflownoOfThread++}length:=len(logsSlice)//traversethechunkfori:=0;i