硬核,图解bufio包系列之写入原理篇

332次阅读  |  发布于2年以前

大家好, 今天我们继续讲解bufio的读写机制。本系列计划用三篇文章来写:[bufio包的读取原理] 、bufio包的写入原理以及使用bufio高效读取的示例。今天跟大家分享第二篇写入原理。

01 Go中文件的普通写入方式

在Go中,普通的写入方式是将内容通过io.Writer直接写入到目标文件中。如下图:

下面是普通的读取文件内容的示例代码:

package main

import (
    "fmt"
    "os"
)


func main() {
    filename := "./test.txt"
    //以读写模式打开文件
    fd, _ := os.OpenFile(filename, os.O_RDWR, 0666)

    body := "Hello World"
    // 从文件中读取最多len(b)个字节的内容到b中
    n, err := fd.Write([]byte(body))
    fmt.Printf("n:=%d, err=%+v\n", n, err)
}

上面的写入方式是通过文件系统的IO进行写入的。其优点是每次写入都是直接持久化到文件中,写入后内容不会丢失。缺点是每次都需要一次底层的系统调用,若需要连续多次写入,那么这种方式的效率同样会大大降低。

02 使用bufio写入到缓冲区

提高写入效率的方式依然是使用缓存,其基本思想是先将内容写入到一大块的缓存中,然后再一次性将缓冲区的内容写入到文件中,以便减少系统调用的次数,以提高写入效率。如下图所示:

这里需要注意的就是缓冲区要较大,默认是1024K。要写入的内容一般较小,并需要多次写入到缓冲区,先在缓冲区暂存内容,然后再一次性将内容写入到目标中,这样才能减少系统调用的次数。其缺点就是有丢失数据的风险,比如缓冲区中的数据在没持久化到目标中之前突然宕机了。

下面我们介绍下在写入操作中缓冲区的组成部分。

03 缓冲区的组成

在写入操作中,会将缓冲区分成两部分:已缓冲了数据的区域和空闲区域两部分。这两部分使用一个位置标识n进行分隔。如下:

在bufio.Writer结构中有两个函数:

在写入时,通过比较要写入内容的字节数和这两个状态的大小来决定具体的写入机制。下面具体介绍要写入内容的不同大小的实现机制。

04 bufio的写入机制

这里主要是根据要写入内容的大小、缓冲区已缓冲区数据的大小以及缓冲区空闲空间大小来决定使用的状态。

场景一:要写入内容的数据量小于缓冲区的可用空间

简单来说就是缓冲区里有足够的空间可以容纳下本次要写入的内容,这时bufio是将数据写入到缓冲区的,而非目标文件中。所以这里要注意 最后一定要记着调用Flush函数,以便将缓冲区的内容输出到目标文件。我们以缓冲区5字节、要写入3字节为例,初始状态如下图: 上图中,我们还有4个字节的空闲空间,现在要写入3个字节的数据。缓冲区的空闲空间可以容纳下要写入的数据,则直接将数据写入缓冲区,如下图:

这时数据只是写入了缓冲区暂存,还没有持久化到目标文件中,如果这时不再继续写入了,那么一定要记着调用Flush函数将缓冲区内容持久化到目标文件中。接下来我们看继续写入的场景。

场景二:要写入内容的数据量大于缓冲区的可用空间

由上图可知,此时的空闲空间只有1个字节。如果我们继续写入2个字节,即超过了缓冲区的空闲空间,如下图:

因为缓冲区里还有内容,这时需要先将缓冲区填满,即将写入内容的1个字节拷贝到缓冲区,如下图:

这时,缓冲区满了,同时判断到还有数据没写完,所以需要将缓冲区中的内容都要主动地刷新到目标文件中,以腾出缓冲区空间,继续接收后面未写入的内容,如下图: 将缓冲区中的内容全部刷新到目标文件后,现在缓冲区实际处于空的状态,可以继续接收剩余的写入数据了。将剩余的1字节内容再次写入缓冲区,如下图: 这里注意,最后1个字节还是在缓冲区中存储着,并没有写入到目标文件。如果想要将该字节写入到目标文件,则需要调用者主动调用一次bufio.Writer的Flush方法,以便将缓冲区中的内容写入到目标文件。

以上缓冲区都是在写入之前就已经存在内容了,下面我们讲解一种特殊的场景,在写入之前缓冲区为空时的策略。

场景三:写入前缓冲区为空

当缓冲区为空时,一种是要写入的数据大小比缓冲区小,这种和上面场景一是一样的。还有一种情况是要写入的内容大于缓冲区的内容,这种情况是绕过缓冲区,直接将内容写入到目标文件。如下图所示:

05 总结

上面我们讲解了每种场景下的写入机制。现在我们来总结一下:

另外还有一点需要注意,在使用时,调用者在调用了bufio.Writer.Write方法后,最后一定要调用一次bufio.Writer.Flush方法,将bufio中缓冲区暂存的内容写入到目标文件中,以免造成缓冲区的数据丢失。

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8