当今世界的任何计算机系统,每天都会生成大量的日志或数据。随着系统的发展,将调试数据存储到数据库中是不可行的,因为它们是不可变的,并且只能用于分析和解决故障。所以,大部分公司倾向于将日志存储在文件中,而这些文件通常位于本地磁盘中。f, err := os.Open(fileName) if err != nil { fmt.Println("cannot able to read the file", err) return } // UPDATE: close after checking error defer file.Close() //Do not forget to close the file 打开文件后,我们有以下两个选项可以选择:逐行读取文件,这有助于减少内存紧张,但需要更多的时间。一次将整个文件读入内存并处理该文件,这将消耗更多内存,但会显著减少时间。由于文件太大,即16 GB,因此无法将整个文件加载到内存中。但是第一种选择对我们来说也是不可行的,因为我们希望在几秒钟内处理文件。但你猜怎么着,还有第三种选择。瞧……相比于将整个文件加载到内存中,在Go语言中,我们还可以使用bufio.NewReader()将文件分块加载。r := bufio.NewReader(f) for { buf := make([]byte,4*1024) //the chunk size n, err := r.Read(buf) //loading chunk into buffer buf = buf[:n] if n == 0 {
if err != nil { fmt.Println(err) break } if err == io.EOF { break } return err } } 一旦我们将文件分块,我们就可以分叉一个线程,即Go routine,同时处理多个文件区块。上述代码将修改为://sync pools to reuse the memory and decrease the preassure on Garbage Collector linesPool := sync.Pool{New: func()interface{} { lines := make([]byte, 500*1024) return lines }} stringPool := sync.Pool{New: func()interface{} { lines := "" return lines }} slicePool := sync.Pool{New: func()interface{} { lines := make([]string, 100) return lines }} r := bufio.NewReader(f) var wg sync.WaitGroup //wait group to keep track off all threads for {
buf := linesPool.Get().([]byte) n, err := r.Read(buf) buf = buf[:n] if n == 0 { if err != nil { fmt.Println(err) break } if err == io.EOF { break } return err } nextUntillNewline, err := r.ReadBytes('\n')//read entire line
if err != io.EOF { buf = append(buf, nextUntillNewline...) }
wg.Add(1) gofunc() {
//process each chunk concurrently //start -> log start time, end -> log end time