init commit
This commit is contained in:
310
archive/go/cache/simple.go
vendored
Normal file
310
archive/go/cache/simple.go
vendored
Normal file
@ -0,0 +1,310 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"hash/fnv"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type lru struct {
|
||||
prev *lru
|
||||
next *lru
|
||||
key string
|
||||
}
|
||||
|
||||
type simpleCache struct {
|
||||
sync.Mutex
|
||||
|
||||
maxMemory int
|
||||
memory int
|
||||
|
||||
hashtable []*entry
|
||||
|
||||
lruhead, lrutail *lru
|
||||
lruHash map[string]*lru
|
||||
|
||||
num int
|
||||
cap int
|
||||
}
|
||||
|
||||
func NewSimpleCache() Cache {
|
||||
return newSimpleCache()
|
||||
}
|
||||
|
||||
func newSimpleCache() *simpleCache {
|
||||
c := &simpleCache{
|
||||
hashtable: make([]*entry, 1024),
|
||||
cap: 1024,
|
||||
lruhead: &lru{},
|
||||
lrutail: &lru{},
|
||||
lruHash: map[string]*lru{},
|
||||
}
|
||||
c.lruhead.next = c.lrutail
|
||||
c.lrutail.prev = c.lruhead
|
||||
go func() {
|
||||
// 扫描过期
|
||||
for {
|
||||
time.Sleep(time.Minute)
|
||||
c.Lock()
|
||||
for n, p := range c.hashtable {
|
||||
prev := p
|
||||
for p != nil {
|
||||
if p.expired() {
|
||||
if p == c.hashtable[n] {
|
||||
c.hashtable[n] = p.next
|
||||
} else {
|
||||
prev.next = p.next
|
||||
}
|
||||
c.num--
|
||||
c.memory -= sizeof(p.value)
|
||||
}
|
||||
prev = p
|
||||
p = p.next
|
||||
}
|
||||
}
|
||||
c.Unlock()
|
||||
}
|
||||
}()
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *simpleCache) SetMaxMemory(size string) bool {
|
||||
if len(size) < 3 {
|
||||
return false
|
||||
}
|
||||
num := size[:len(size)-2]
|
||||
unit := size[len(size)-2:]
|
||||
maxMemory, err := strconv.Atoi(num)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if maxMemory == 0 {
|
||||
return false
|
||||
}
|
||||
switch strings.ToUpper(unit) {
|
||||
case "KB":
|
||||
c.maxMemory = maxMemory << 10
|
||||
case "MB":
|
||||
c.maxMemory = maxMemory << 20
|
||||
case "GB":
|
||||
c.maxMemory = maxMemory << 30
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *simpleCache) hash(key string) int {
|
||||
hash := fnv.New64()
|
||||
hash.Write([]byte(key))
|
||||
nk := int(hash.Sum64())
|
||||
if nk < 0 {
|
||||
nk = -nk
|
||||
}
|
||||
return nk % c.cap
|
||||
}
|
||||
|
||||
func (c *simpleCache) rehashkey(cap int, key string) int {
|
||||
hash := fnv.New64()
|
||||
hash.Write([]byte(key))
|
||||
nk := int(hash.Sum64())
|
||||
if nk < 0 {
|
||||
nk = -nk
|
||||
}
|
||||
return nk % cap
|
||||
}
|
||||
|
||||
func (c *simpleCache) rehash() {
|
||||
//扩容
|
||||
newCap := c.cap * 2
|
||||
tmpHashtable := make([]*entry, newCap)
|
||||
for _, p := range c.hashtable {
|
||||
for p != nil {
|
||||
n := c.rehashkey(newCap, p.key)
|
||||
nv := tmpHashtable[n]
|
||||
nk := &entry{
|
||||
key: p.key,
|
||||
value: p.value,
|
||||
expireTime: p.expireTime,
|
||||
operateTime: p.operateTime,
|
||||
next: nil,
|
||||
}
|
||||
if nv == nil {
|
||||
tmpHashtable[n] = nk
|
||||
} else {
|
||||
for nv.next != nil {
|
||||
nv = nv.next
|
||||
}
|
||||
nv.next = nk
|
||||
}
|
||||
p = p.next
|
||||
}
|
||||
}
|
||||
c.cap = newCap
|
||||
c.hashtable = tmpHashtable
|
||||
}
|
||||
|
||||
func (c *simpleCache) Set(key string, val interface{}, expire time.Duration) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
defer c.lru(key)
|
||||
|
||||
c.memory += sizeof(val)
|
||||
|
||||
if float32(c.num)/float32(c.cap) > 0.75 {
|
||||
c.rehash()
|
||||
}
|
||||
|
||||
n := c.hash(key)
|
||||
expireTime := time.Now()
|
||||
if expire == 0 {
|
||||
// 设置一个很大的时间
|
||||
expireTime = expireTime.Add(time.Hour * 24 * 365 * 10)
|
||||
} else {
|
||||
expireTime = expireTime.Add(expire)
|
||||
}
|
||||
p := c.hashtable[n]
|
||||
e := &entry{
|
||||
key: key,
|
||||
value: val,
|
||||
expireTime: expireTime,
|
||||
next: nil,
|
||||
}
|
||||
if p == nil {
|
||||
c.hashtable[n] = e
|
||||
c.num++
|
||||
return
|
||||
}
|
||||
prev := p
|
||||
for p != nil {
|
||||
if p.key == key {
|
||||
p.value = val
|
||||
p.expireTime = expireTime
|
||||
return
|
||||
}
|
||||
prev = p
|
||||
p = p.next
|
||||
}
|
||||
prev.next = e
|
||||
c.num++
|
||||
}
|
||||
|
||||
func (c *simpleCache) lru(key string) {
|
||||
l := c.lruHash[key]
|
||||
if l == nil {
|
||||
// 插入头
|
||||
nl := &lru{prev: c.lruhead, next: c.lruhead.next, key: key}
|
||||
c.lruhead.next.prev = nl
|
||||
c.lruhead.next = nl
|
||||
c.lruHash[key] = nl
|
||||
} else {
|
||||
// 移动到头
|
||||
l.prev.next = l.next
|
||||
l.next.prev = l.prev
|
||||
l.prev = c.lruhead
|
||||
l.next = c.lruhead.next
|
||||
c.lruhead.next.prev = l
|
||||
c.lruhead.next = l
|
||||
}
|
||||
// 清理内存
|
||||
for c.memory > c.maxMemory && c.num > 1 {
|
||||
if c.lrutail.prev == c.lruhead {
|
||||
return
|
||||
}
|
||||
rmk := c.lrutail.prev
|
||||
c.lrutail.prev.next = c.lrutail
|
||||
c.lrutail.prev = c.lrutail.prev.prev
|
||||
c.del(rmk.key)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *simpleCache) delLru(key string) {
|
||||
l := c.lruHash[key]
|
||||
if l != nil {
|
||||
l.prev.next = l.next
|
||||
l.next.prev = l.prev
|
||||
delete(c.lruHash, key)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *simpleCache) Get(key string) (interface{}, bool) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
n := c.hash(key)
|
||||
p := c.hashtable[n]
|
||||
prev := p
|
||||
for p != nil {
|
||||
if p.key == key {
|
||||
// 过期删除
|
||||
if p.expired() {
|
||||
c.delLru(key)
|
||||
if c.hashtable[n] == p {
|
||||
c.hashtable[n] = p.next
|
||||
} else {
|
||||
prev.next = p.next
|
||||
}
|
||||
c.num--
|
||||
c.memory -= sizeof(p.value)
|
||||
return nil, false
|
||||
}
|
||||
c.lru(key)
|
||||
return p.value, true
|
||||
}
|
||||
prev = p
|
||||
p = p.next
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (c *simpleCache) Del(key string) bool {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
return c.del(key)
|
||||
}
|
||||
|
||||
func (c *simpleCache) del(key string) bool {
|
||||
n := c.hash(key)
|
||||
p := c.hashtable[n]
|
||||
prev := p
|
||||
for p != nil {
|
||||
if p.key == key {
|
||||
c.delLru(key)
|
||||
if c.hashtable[n] == p {
|
||||
c.hashtable[n] = p.next
|
||||
} else {
|
||||
prev.next = p.next
|
||||
}
|
||||
c.num--
|
||||
c.memory -= sizeof(p.value)
|
||||
return true
|
||||
}
|
||||
prev = p
|
||||
p = p.next
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *simpleCache) Exists(key string) bool {
|
||||
_, ok := c.Get(key)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (c *simpleCache) Flush() bool {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
c.num = 0
|
||||
c.memory = 0
|
||||
c.hashtable = make([]*entry, c.cap)
|
||||
c.lruhead = &lru{}
|
||||
c.lrutail = &lru{}
|
||||
c.lruHash = map[string]*lru{}
|
||||
c.lruhead.next = c.lrutail
|
||||
c.lrutail.prev = c.lruhead
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *simpleCache) Keys() int64 {
|
||||
return int64(c.num)
|
||||
}
|
Reference in New Issue
Block a user