使用很少的内存,在Go中快速读取大文件的最佳方法

5

我想要从不同的文本或JSONCSV文件中读取数据。我应该采用哪种方法?

我有关于文件读取的博客文章File readRead 2 GB text file with small RAM,介绍了不同的文件读取方法。

不同的方法:

  • 分块读取文件
  • 并发地读取文件块
  • 将整个文件读入内存
  • 将长字符串拆分为单词
  • 逐个单词扫描

什么是在小内存情况下读取文件的最快方法?


5
就像所有最佳性能的问题一样,唯一的方法是在您特定的工作负载下测试不同的选项,并查看哪个表现最佳。 - Jonathan Hall
这篇博客对我来说很不错 https://medium.com/swlh/processing-16gb-file-in-seconds-go-lang-3982c235dfa2 - Seenu S
与关闭投票相对,它本可以是一个倾泻一些额外技巧和窍门的地方。当然,每种情况都是不同的,但也存在一些通用的技巧。 - user4466350
1个回答

23

基本上有两种不同的方法来解析文件:文档解析和流解析。

文档解析读取文件中的数据并将其转换为一组大型对象,您可以像浏览器中的HTML DOM一样查询这些对象。优点是您可以随时获取完整的数据,这通常更简单。缺点是您必须将所有数据存储在内存中。

dom = parse(stuff)

// Now do whatever you like with the DOM

流解析是一种逐个读取元素并立即呈现给您使用的方式,然后继续读取下一个元素。
for element := range stream(stuff) {
    ...examine one element at a time...
}

优点是你不必将整个内容加载到内存中。缺点是你必须随着数据的流动进行处理。这对于搜索或任何需要逐个处理的操作非常有用。

幸运的是,Go提供了处理常见格式的库。

一个简单的例子是处理CSV文件。

package main

import(
    "encoding/csv"
    "fmt"
    "log"
    "os"
    "io"
)

func main() {
    file, err := os.Open("test.csv")
    if err != nil {
        log.Fatal(err)
    }

    parser := csv.NewReader(file)

    ...
}

我们可以将整个内容作为一个大的[][]string读入内存。
records, err := parser.ReadAll()
if err != nil {
    log.Fatal(err)
}

for _,record := range records {
    fmt.Println(record)
}

或者我们可以节省大量内存,一次处理一行。

for {
    record, err := parser.Read()
    if err == io.EOF {
        break
    }
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(record)
}

由于CSV的每一行都具有相同的功能,逐行处理是最合理的。

JSON和XML比较复杂,因为它们是大型嵌套结构,但也可以进行流式处理。在encoding/json文档中有流式处理的示例


如果你的代码不是简单的循环,那该怎么办呢?如果你想利用并发,可以使用通道和 goroutine,使其与程序的其他部分同时进行。
records := make( chan []string )
go func() {
    parser := csv.NewReader(file)

    defer close(records)
    for {
        record, err := parser.Read()
        if err == io.EOF {
            break
        }
        if err != nil {
            log.Fatal(err)
        }

        records <- record
    }
}();

现在您可以将{{records}}传递给一个可以处理它们的函数。
func print_records( records chan []string ) {
    for record := range records {
        fmt.Println(record)
    }
}

1
我创建了一个包,以多线程方式读取和处理CSV文件中的行。 https://github.com/actforgood/bigcsvreader 一些基准测试可以在README中找到。 - Bogdan Constantinescu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接