github地址:https://github.com/hackssssss/lru_expired_cache
之前實現了lru算法的cache,鏈接,後來發現可以添加key自動過期的策略,採用惰性過期策略。即當get或者set這個key時,纔去更新key的狀態,如果過期,那麼將其刪除。
package lru_expiredcache
import (
"container/list"
"fmt"
"sync"
"time"
)
type value struct {
data []byte
lruPos *list.Element
expiredTime int64
}
type lruExpiredCache struct {
maxSize int
data map[interface{}]*value
lck *sync.Mutex
lru *list.List
}
func NewCache(maxLength int) *lruExpiredCache {
if maxLength <= 0 {
panic("maxLength can not < 0 in NewCache")
}
return &lruExpiredCache{
maxSize: maxLength,
data: make(map[interface{}]*value),
lck: new(sync.Mutex),
lru: list.New(),
}
}
func (c *lruExpiredCache) Set(key interface{}, data []byte) {
c.lck.Lock()
defer c.lck.Unlock()
//c.updateKey(key) //這裏不用更新狀態,因爲數據即將被覆蓋
if val, found := c.data[key]; found {
c.deleteLruItem(val.lruPos) //刪除原先在list中的位置
val.lruPos = c.updateNewItem(key) // 追加到list末尾,更新位置標示
val.expiredTime = -1
c.data[key] = val
} else {
var pos *list.Element
if len(c.data) < c.maxSize {
pos = c.updateNewItem(key) //直接追加到list末尾
} else {
c.deleteLruItem(c.lru.Front()) //刪除最久未使用的
pos = c.updateNewItem(key) //將本次key更新到list的末尾
}
val := &value{
data: data,
lruPos: pos,
expiredTime: -1,
}
c.data[key] = val
}
}
//lazy刪除,等到實際用的時候判斷key是否應該被刪除。
func (c *lruExpiredCache) updateKey(key interface{}) {
if val, found := c.data[key]; found {
if val.expiredTime <= time.Now().Unix() {
c.deleteLruItem(val.lruPos) //在map中和list中均需要刪除
}
}
}
func (c *lruExpiredCache) Exist (key interface{}) bool {
c.lck.Lock()
defer c.lck.Unlock()
c.updateKey(key)
_, found := c.data[key]
return found
}
func (c *lruExpiredCache) SetWithExpiredTime(key interface{}, data []byte, expiredSeconds int64) {
if expiredSeconds <= 0 {
return
}
c.lck.Lock()
defer c.lck.Unlock()
c.updateKey(key)
expiredTime := time.Now().Unix() + expiredSeconds
if val, found := c.data[key]; found {
c.deleteLruItem(val.lruPos) //刪除原先在list中的位置
val.lruPos = c.updateNewItem(key) // 追加到list末尾,更新位置標示
val.expiredTime = expiredTime
c.data[key] = val
} else {
var pos *list.Element
if len(c.data) < c.maxSize {
pos = c.updateNewItem(key) //直接追加到list末尾
} else {
c.deleteLruItem(c.lru.Front()) //刪除最久未使用的
pos = c.updateNewItem(key) //將本次key更新到list的末尾
}
val := &value{
data: data,
lruPos: pos,
expiredTime: expiredTime,
}
c.data[key] = val
}
}
func (c *lruExpiredCache) Get(key interface{}) (exist bool, data []byte) {
c.lck.Lock()
defer c.lck.Unlock()
c.updateKey(key)
if val, found := c.data[key]; found {
data = val.data
c.deleteLruItem(val.lruPos) //刪除原先在list中的位置
val.lruPos = c.updateNewItem(key) // 追加到list末尾,更新位置標示
c.data[key] = val
exist = true
}
return
}
func (c *lruExpiredCache) deleteLruItem(pos *list.Element) (item *list.Element) {
c.lru.Remove(pos)
delete(c.data, pos.Value)
return
}
func (c *lruExpiredCache) updateNewItem(key interface{}) (item *list.Element) {
item = c.lru.PushBack(key)
return
}
// lazy更新的話,這樣Length就會不準了
//func (c *lruExpiredCache) Length() int {
// c.lck.Lock()
// defer c.lck.Unlock()
// return len(c.data)
//}
func (c *lruExpiredCache) DebugShowMapData() {
c.lck.Lock()
defer c.lck.Unlock()
fmt.Println("=== map ===")
for k, v := range c.data {
fmt.Println(k, v)
}
fmt.Println("=== map over ===")
}
func (c *lruExpiredCache) DebugShowLruList() {
c.lck.Lock()
defer c.lck.Unlock()
fmt.Println("=== list ===")
for v := c.lru.Front(); v != nil; v = v.Next() {
fmt.Print(v.Value, " ")
}
fmt.Println("\n=== list over ===")
}
func (c *lruExpiredCache) Delete(key interface{}) {
c.lck.Lock()
defer c.lck.Unlock()
c.updateKey(key)
if val, found := c.data[key]; found {
c.deleteLruItem(val.lruPos)
}
}