用双向链表实现简易的内存系统

1 篇文章 0 订阅
1 篇文章 0 订阅

思路

1,map+双向链表来实现,双向链表的表头记录最新的数据,
get,set操作都会把节点插入表头,这样的好处是保证热点数据在前面,
非热单数据在后面;

2,定时器按配置定时从链尾开始扫描数据,过期的数据被清除,如果实在没有
过期时间,但是又超出内存限制的情况就删除链尾的最后一个节点

3,哈希表记录每一个节点的数据,set(),get(),exist(),del(),keys()
操作的时间复杂度为常数级别O(1)

4,并发时的线程安全控制用读写锁来实现,即set,del,操作时加上c.lock.Lock(), 读取时加上c.lock.RLock()

5,目前这个只实现了string类型数据的读写管理

interface.go

package cache

import "time"

type CacheInterface interface {

	SetMaxMemory(size string) bool

	Set(key string, val interface{}, expire time.Duration)

	Get(key string) (interface{}, bool)

	Del(key string) bool

	Exists(key string) bool

	Flush() bool

	Keys() int64
}

handle.go

package cache

import (
	"fmt"
	"log"
	"sync"
	"time"
)

type Cache struct {
	Head *listNode
	Tail *listNode
	MemoryUsed int64
	Maxmemory  int64
	M maps
	Count int64
	lock sync.RWMutex
}

type listNode struct {
	key string
	val interface{} //节点数据
	expire time.Time //过期时间
	pre  *listNode   //前一个节点地址
	next *listNode   //后一个节点地址
}

type maps map[string]*listNode

func (m maps)Init() maps{
	cm := make(map[string]*listNode)
	return cm
}

func (m maps) Set(key string, p *listNode) {
	m[key] = p
}

func (m maps) Get(key string) (*listNode, bool) {
	if p, ok := m[key]; ok {
		return p,ok
	}

	return nil, false
}

func (m maps) Detele(key string) {
	delete(m,key)
}

func (c *Cache) GetCurrent(current *listNode) interface{} {
	return current.val
}

func (c *Cache) SetMaxMemory(size string) bool {
	if len(size) == 0 {
		return false
	}
	switch size {
	case "1kb":
		c.Maxmemory = KB1
	case "1mb":
		c.Maxmemory = MB1
	case "2mb":
		c.Maxmemory = MB2
	case "1gb":
		c.Maxmemory = GB1
	}

	return true
}


func (c *Cache) Init() *Cache{
	c.Head = &listNode{}
	c.Tail = &listNode{}
	//建头尾两个为节点,初始状态为首尾相连
	c.Head.next = c.Tail
	c.Head.pre  = nil
	c.Tail.pre  = c.Head
	c.Tail.next = nil
	c.Count = 0
	c.MemoryUsed = 0
	c.lock = sync.RWMutex{}
	//初始化map
	pm := &maps{}
	c.M = pm.Init()
	return c
}

func (c *Cache) addCount() int64 {
	c.Count ++
	return c.Count
}

func (c *Cache) decCount() int64 {
	c.Count --
	return c.Count
}


func (c *Cache) Set(key string, val interface{}, expire time.Duration) {
	c.lock.Lock()
	defer c.lock.Unlock()
	if val == nil {
		return
	}

	// 查看内存配置,如果超过就遍历链表释放内存
	if c.MemoryUsed + int64(len(val.(string))) >= c.Maxmemory {
		c.Lru()
	}

	//再次检查,如果超过内存配置就不能写
	if c.MemoryUsed + int64(len(val.(string))) >= c.Maxmemory {
		return
	}

	// 查找Map,如果map存在,则从map拿出所在的位置(指针),然后插入表头,同时调整链表前后节点的位置
	if v,ok := c.M.Get(key); ok {
		c.takeOut(v)
		v.val = val
		c.insertToHead(v)
		fmt.Printf("Set key:%s,val:%v,first:%v\n",key,v.val,c.Head.next.val)
	}else{//如果不存在,则新建一个节点插入表头
		node := &listNode{}
		node.key = key
		node.val = val
		now := time.Now()
		node.expire = now.Add(time.Duration(expire)*time.Second)
		c.insertToHead(node)
		//个数统计
		c.addCount()
		// 把最新的位置写入map
		c.M.Set(key,node)
		//内存统计
		c.incMemoryUsed(node)
	}
}

func (c *Cache) Get(key string) (interface{}, bool) {
	c.lock.RLock()
	defer c.lock.RUnlock()
	// 1,先从map中找,如果不存在,返回false
	current,ok := c.M.Get(key)
	if !ok {
		return nil,false
	}
	// 2,如果存在,找出相应的位置,并检查key是否过期
	now := time.Now()
	if now.After(current.expire) {
		//如果key已经过期,再删除Key,即调整链表的前后位置,返回false
		c.takeOut(current)
		//清除map表中的Key
		c.M.Detele(key)
		//扣减相应的内存
		c.decMemoryUsed(current)
	}else{
		//如果key还未过期,调整位置,把此key的位置插入表头,并更新所在map的位置
		if c.Head.next != current {
			//先抽出节点
			c.takeOut(current)
			//把抽出的节点放到第表头
			c.insertToHead(current)
		}
	}

	return current.val,true
}

func (c *Cache) Del(key string) bool {
	c.lock.Lock()
	defer c.lock.Unlock()
	// 1,查找map,如果存在再往下处理
	current, ok := c.M.Get(key)
	if !ok {
		return false
	}
	fmt.Printf("Del:key:%s,val:%v\n",key,current.val)
	// map存在,找出相应的地址,调整链表前后节点的位置
	c.takeOut(current)

	// 删除在map中的key
	c.M.Detele(key)

	//减少一个
	c.decCount()

	//内存统计
	c.decMemoryUsed(current)

	return true
}

func (c *Cache) Exists(key string) bool {
	// 查找map 看是否存在,返回对应的true或false
	if _,ok := c.M.Get(key); ok {
		return true
	}else{
		return false
	}
}

func (c *Cache) Flush() bool {
	c.lock.Lock()
	defer c.lock.Unlock()
	//清空map
	for key := range c.M {
		c.M.Detele(key)
	}

	return true
}

func (c *Cache) Keys() int64 {
	return c.Count
}

func (c *Cache) size(current *listNode) int64 {
	strlen := len(current.val.(string)) + len(current.key) + 24 + 8 + 8
	return int64(strlen)
}

func (c *Cache) Lru() {
	log.Println("Lru start....")
	log.Printf("Berfore Lru memoryUsed:%d,count:%d,keys\n",c.MemoryUsed,c.Count)
	// 遍历链表,从表尾开始遍历
	now     := time.Now()
	current := c.Tail
	m       := c.M

	count := 0
	//从表尾开始,清除过期的数据
	for current.pre != nil {
		if now.After(current.expire) {
			if current.val != nil {
				c.takeOut(current)
				key := current.key
				m.Detele(key)
				c.decMemoryUsed(current)
				c.decCount()
				count ++
				log.Printf("Clear data key:%s,val:%s\n",key,current.val)
			}
		}
		current = current.pre
	}

	//如果一个都没有过期,则删除最后一个节点
	if count == 0 && c.MemoryUsed >= c.Maxmemory{
		current := c.Tail.pre
		if current.val != nil {
			c.takeOut(current)
			key := current.key
			m.Detele(key)
			c.decMemoryUsed(current)
			c.decCount()
			log.Printf("Clear data key:%s,val:%s\n",key,current.val)
		}
	}

	log.Println("Lru ending....")
	log.Printf("After Lru memoryUsed:%d,count:%d\n",c.MemoryUsed,c.Count)
}

//内存增加
func (c *Cache) incMemoryUsed(current *listNode) {
	strlen := c.size(current)
	c.MemoryUsed = c.MemoryUsed + int64(strlen)
}

//内存扣减
func (c *Cache) decMemoryUsed(current *listNode) {
	strlen := c.size(current)
	c.MemoryUsed = c.MemoryUsed - int64(strlen)
}

//取出一个节点
func (c *Cache) takeOut(current *listNode) {
	pre  := current.pre
	next := current.next
	pre.next = next
	next.pre = pre
}

//把节点插入链头
func (c *Cache) insertToHead(current *listNode) {
	oldFirst := c.Head.next
	oldFirst.pre = current
	current.next = oldFirst
	current.pre = c.Head
	c.Head.next = current
}

func (c *Cache) Run(){
	dt := time.Duration(time.Second * CLEAR_DATA_TIMER)
	t := time.NewTicker(dt)
	defer t.Stop()
	for{
		select {
			case <-t.C :
				c.Lru()
		}
	}
}

main.go

func main() {
	var ch cache.CacheInterface
	p := &cache.Cache{}
	c := p.Init()

	ch = c
	ch.SetMaxMemory("1kb")
	ch.Set("test1","garyyang19999sdfffffffffffffffff",300)
	record,ok := ch.Get("test1")
	fmt.Println(record,ok)

	ch.Set("test2","garyyang19999sdfffffffffffffffff",300)
	fmt.Printf("ch.Exits(test2):%v\n",ch.Exists("test2"))
	ch.Set("test4","test4",4)
	ch.Set("test5","test5",5)
	ch.Set("test6","test6",6)
	ch.Set("test7","test7",7)
	ch.Set("test8","test8",8)

	go c.Run()

	sigs := make(chan os.Signal)
	signal.Notify(sigs)
	<- sigs
}	
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值