一、介绍
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次方。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。