上一个文件我们对go读文件的方式有所了解,这个我们深入一下读文件,我们如何对大文件进行读取呢?我们需要从耗时,性能等方面来考虑。
首先我们先看下读10m文件和1.3g的文件,用readAll的耗时和内存的使用。
一个16m的文件
-rw-r--r-- 1 13727 197609 12M Mar 13 18:41 stat.log.2021-03-10-16
一个1.3g的文件
-rw-r--r-- 1 13727 197609 1.3G Mar 14 21:10 stat.log.2021-03-10-167
运行如下代码执行
代码语言:javascript复制package main
import (
"fmt"
"io/ioutil"
"os"
"runtime"
"time"
)
func main() {
//读文件
file, err := os.Open("E://stat.log.2021-03-10-16")
if err != nil {
fmt.Println("读文件失败", err)
return
}
defer file.Close()
t1 := time.Now().UnixNano()
content, err := ioutil.ReadAll(file)
if err != nil {
fmt.Println("读内容失败", err)
return
}
fmt.Println(len(string(content)))
t2 := time.Now().UnixNano()
fmt.Println((float64(t2) - float64(t1)) / 1000000000)
traceMemStats()
}
func traceMemStats() {
var ms runtime.MemStats
runtime.ReadMemStats(&ms)
fmt.Printf("TotalAlloc:%d(m) Alloc:%d(m)", ms.TotalAlloc/1024/1024, ms.Alloc/1024/1024)
}
16m文件执行结果如下:
代码语言:javascript复制字节长度:11588920
0.02125312
TotalAlloc:43(m) Alloc:35(m)
1.3g的文件执行如下:
代码语言:javascript复制字节长度:1321162080
2.37205248
TotalAlloc:5356(m) Alloc:4332(m)
上面的结果很显然,耗时比较长,而且内存占用高。针对于这样的情况,我们可以使用sync.pool,使用bufio.NewReader来实现读取。
我们sync.pool主要是复用内存,让内存不占用那么高,sync.pool之前我有文章讲过,这个很适合读文件的这个功能。
我们先看下代码:
代码语言:javascript复制package main
import (
"bufio"
"fmt"
"io"
"os"
"runtime"
"sync"
"time"
)
func main() {
//读文件
file, err := os.Open("E://stat.log.2021-03-10-16")
if err != nil {
fmt.Println("读文件失败", err)
return
}
t1 := time.Now().UnixNano()
linesPool := sync.Pool{New: func() interface{} {
lines := make([]byte, 12*2048)
return lines
}}
var r *bufio.Reader
r = bufio.NewReader(file)
content := []byte{}
for {
buf := linesPool.Get().([]byte)
n, err := r.Read(buf)
buf = buf[:n]
if n == 0 {
if err == io.EOF {
break
}
if err != nil {
break
}
return
}
content = append(content, buf...)
}
fmt.Println("字节长度:", len(string(content)))
t2 := time.Now().UnixNano()
fmt.Println((float64(t2) - float64(t1)) / 1000000000)
traceMemStats1()
}
func traceMemStats1() {
var ms runtime.MemStats
runtime.ReadMemStats(&ms)
fmt.Printf("Alloc:%d(m) HeapIdle:%d(m) HeapReleased:%d(m)", ms.Alloc/1024/1024, ms.HeapIdle/1024/1024, ms.HeapReleased/1024/1024)
}
16m文件执行结果如下:随着我们设置sync.pool的大小,内存占用会不一样,sync.pool设置越大,堆内存占用的就越少。
代码语言:javascript复制字节长度:11588920
0.181612032
TotalAlloc:89(m) Alloc:24(m)
1.3g的文件执行如下:sync.pool的大小设置,对内存占用影响比较大,gc回收的速度也会受影响。
代码语言:javascript复制字节长度:1321162080
4.201924352
TotalAlloc:9893(m) Alloc:2815(m)
单从耗时上面看ReadAll是比较好的选择,但是ReadAll有一个弊端,如果读超大文件,内存不容易被释放,这样会造成内存占用比较久,bufio读文件占用总内存比较多,整体来说使用内存是比较少的,如果线上用不容易出问题,耗时也还是比较乐观的。
为什么会有如此的差异呢?下次我们从原理上面来分析。