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 是基于字節(jié)數組的非線程安全隊列類型的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字節(jié)存儲這個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字節(jié)保存了 data的長度。
func (q *BytesQueue) push(data []byte, len int) { headerEntrySize := binary.PutUvarint(q.headerBuffer, uint64(len)) q.copy(q.headerBuffer, headerEntrySize) // 用一個8字節(jié)保存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個字節(jié),2的64次方。
以上就是go內存緩存BigCache實現BytesQueue源碼解讀的詳細內容,更多關于go內存緩存BigCache BytesQueue的資料請關注腳本之家其它相關文章!
相關文章
golang?struct?json?tag的使用以及深入講解
這篇文章主要給大家介紹了關于golang?struct?json?tag的使用以及深入講解,文中通過實例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2022-02-02關于golang?struct?中的?slice?無法原子賦值的問題
這篇文章主要介紹了為什么?golang?struct?中的?slice?無法原子賦值的問題,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2024-01-01