go内存缓存BigCache实现BytesQueue源码解读

 更新时间:2023年09月05日 15:35:12   作者:海生  
这篇文章主要为大家介绍了go内存缓存BigCache实现BytesQueue源码解读,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

一、BytesQueue结构

BytesQueue结构,是bigcache真正数据存储的地方。

值得注意的是删除缓存元素的时候bigcache只是在map[uint64]uint32中删除了它的索引,byte数组里的空间是不会释放的。

在 bigCache 中,所有的 value 都是存在一个 BytesQueue 中的,从实现可知,所有的用户存储数据经由序列化后存入 array []byte

// BytesQueue is a non-thread safe queue type of fifo based on bytes array.
// BytesQueue 是基于字节数组的非线程安全队列类型的FIFO。
// For every push operation index of entry is returned. It can be used to read the entry later
// 对于每个推送操作索引,都会返回。它可用于稍后阅读条目。
type BytesQueue struct {
    full         bool
    array        []byte // 真正存储数据的地方
    capacity     int    // array 的容量
    maxCapacity  int    // array 可申请的最大容量
    head         int
    tail         int // 下次可以插入 item 的位置
    count        int // 当前插入的 item 数量
    rightMargin  int
    headerBuffer []byte // 插入前做临时 buffer 所用(slice-copy)
    verbose      bool   // 打印 log 开关
}

初始化BytesQueue方法

func NewBytesQueue(capacity int, maxCapacity int, verbose bool) *BytesQueue {
    return &BytesQueue{
        array:        make([]byte, capacity), // 真正存储数据的地方,长度为capacity,直接初始化每个值
        capacity:     capacity,
        maxCapacity:  maxCapacity,
        headerBuffer: make([]byte, binary.MaxVarintLen32),
        tail:         leftMarginIndex,
        head:         leftMarginIndex,
        rightMargin:  leftMarginIndex,
        verbose:      verbose,
    }
}

我们通过维护下面几个变量来实现存储位移及标识:

head:起始位置(也可以理解为,当前最老的数据的位置,删除的逻辑从这个位置开始)

tail:下次可以插入 item 的位置

capacity:标识 array 的容量

count:当前已经插入的 item 的数量

maxCapacity:标识 array 可以申请的最大容量

rightMargin:用于标识队列中最后一个元素的位置,是一个绝对位置。

leftMarginIndex:常量,值为 1,标识队列的开头位置(0 号不用)

注意, head 和 tail 以及 rightMargin 的初始值都是 leftMarginIndex。BytesQueue 使用 []byte 类型来模拟队列,插入数据从 tail 位置,删除数据从 head 位置。为标准的FIFO队列。

二、如何使用这个BytesQueue

1、插入item到队列,通过调用BytesQueue.Push([]byte) 方法,我们可以把[]byte类型的数据插入到BytesQueue中。

返回为这个值存储的index索引。

func TestQueuePush(t *testing.T) {
    // 初始化一个byte队列
    queue := NewBytesQueue(5, 0, false)
    t.Log(queue) // &{false [0 0 0 0 0] 5 0 1 1 0 1 [0 0 0 0 0] false}
    // 调用Push方法,会返回获取这个值的index索引
    index, err := queue.Push([]byte("a"))
    t.Log(index, err) // 1 <nil>
    index, err = queue.Push([]byte("b"))
    t.Log(index, err) // 3 <nil>
    // 通过index索引就可以获取到这个值
    a, err2 := queue.Get(1)
    t.Log(string(a), err2) // a <nil>
    b, err2 := queue.Get(3)
    t.Log(string(b), err2) // b <nil>
}

这样,我们就相当于一个值,和一个索引index对应了。

通过一个索引可以快速的获取到这个值。

bigcache我们通过BytesQueue,存储数据。

再用一个map,记录一下这个index和值的 对应关系。

就可O(1)的时间复杂度,查询BytesQueue的所有数据。

-----注意:为什么通过index可以获取value的值?

因为我们再push的 []byte的时候,最终存储这个[]byte会用一个8字节存储这个entry的长度。

这样通过index我们获取到这个长度,然后就可以获取到这个数据。

func (q *BytesQueue) Push(data []byte) (int, error) {
    neededSize := getNeededSize(len(data))
    ....... // 省略
    index := q.tail

    q.push(data, neededSize)

    return index, nil
}

从Push()方法中,我们看到调用了一个push()方法。我们打开源代码,可以看到最终在保存数据的时候,先用一个8字节保存了 data的长度。

func (q *BytesQueue) push(data []byte, len int) {
    headerEntrySize := binary.PutUvarint(q.headerBuffer, uint64(len))
    q.copy(q.headerBuffer, headerEntrySize) // 用一个8字节保存data的长度
    q.copy(data, len-headerEntrySize)       // 写入data
    if q.tail > q.head {
        q.rightMargin = q.tail
    }
    if q.tail == q.head {
        q.full = true
    }
    q.count++
}

byteQueue中每个元素都有2部分组成,前8个byte是数据的长度,后面是数据的值本身,每个byteQueue中每个元素的最大长度是8个字节,2的64次方。

以上就是go内存缓存BigCache实现BytesQueue源码解读的详细内容,更多关于go内存缓存BigCache BytesQueue的资料请关注脚本之家其它相关文章!

相关文章

  • Go语言中的方法、接口和嵌入类型详解

    Go语言中的方法、接口和嵌入类型详解

    这篇文章主要介绍了Go语言中的方法、接口和嵌入类型详解,本文分别对它们做了详细讲解,需要的朋友可以参考下
    2014-10-10
  • Go语言中的字符串拼接方法详情

    Go语言中的字符串拼接方法详情

    本文介绍Go语言中的string类型、strings包和bytes.Buffer类型,介绍几种字符串拼接方法的相关资料,需要的朋友可以参考一下,希望对你有所帮助
    2021-10-10
  • 使用Go进行单元测试的实现

    使用Go进行单元测试的实现

    这篇文章主要介绍了使用Go进行单元测试的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-11-11
  • Go语言无缓冲的通道的使用

    Go语言无缓冲的通道的使用

    Go语言中无缓冲的通道是指在接收前没有能力保存任何值的通道,本文主要介绍了Go语言无缓冲的通道的使用,具有一定的参考价值,感兴趣的可以了解一下
    2024-01-01
  • Go 库bytes.Buffer和strings.Builder使用及性能对比

    Go 库bytes.Buffer和strings.Builder使用及性能对比

    这篇文章主要为大家介绍了Go 库bytes.Buffer和strings.Builder使用及性能对比,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • 详解Golang时间处理的踩坑及解决

    详解Golang时间处理的踩坑及解决

    在各个语言之中都有时间类型的处理,这篇文章主要和大家分享一下Golang进行时间处理时哪里最容易踩坑以及解决方法,需要的可以参考一下
    2023-01-01
  • Go 语言入门学习之正则表达式

    Go 语言入门学习之正则表达式

    这篇文章主要介绍了Go 语言入门学习之正则表达式,文章基于GO语言的相关资料展开详细内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-04-04
  • go语言题解LeetCode1160拼写单词示例详解

    go语言题解LeetCode1160拼写单词示例详解

    这篇文章主要为大家介绍了go语言题解LeetCode1160拼写单词示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • go获取协程(goroutine)号的实例

    go获取协程(goroutine)号的实例

    这篇文章主要介绍了go获取协程(goroutine)号的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • 利用Golang解析json数据的方法示例

    利用Golang解析json数据的方法示例

    Go提供了原生的JSON库,并且与语言本身有效的集成在了一起。下面这篇文章将给大家介绍关于利用Golang解析json数据的方法,文中给出了详细的示例代码供大家参考学习,需要的朋友们下面跟着小编来一起学习学习吧。
    2017-07-07

最新评论