NoSql 数据库那么火 那么就让我们来写一个吧
单机存储模块 就是有一个以 map 映射键值对 存储 key value 关系的一个 map 在 redis中 没map数据类型 所以是自己定义的 在go中 是由hashmap这个数据结构的所以 就很简单了。
//存储value 包含 内容 和超时 时间
type Item struct {
Object interface{} //核心 用来存储数据
Expiration int64 //每个 键值对 都存在超时时间
}
//item 方法 用来判断有没有过期
func (it Item) Expired() bool{
if it.Expiration == 0 {
return false
}
//如果 当前 时间 大于过期时间 则 过期
return time.Now().UnixNano() > it.Expiration
}
const (
//不会过期的标志
NoExpiration time.Duration = -1
//默认的过期时间
DefaultExpiration time.Duration = 0
)
用 -1 和 0 代表 2 中kv 的类型 一种是永不过期 一种是会过期的
type Cache struct {
defaultExpiration time.Duration //默认的过期时间
items map[string]Item //缓存数据存储
mu sync.RWMutex //读写锁
gcInterval time.Duration //过期数据清理周期
stopGc chan bool//需要暂停垃圾回收时 触发
}
items map[string]Item 将 一个键 对应 一个Item 用于存放数据
对于过期 的 map里的内容好会单独启动一个携程进行删除
func (c *Cache) gcLoop(){
fmt.Println("GC Running.....")
ticker := time.NewTicker(c.gcInterval) //定期执行 gc清除过期数据
for{
select {
case <- ticker.C:
fmt.Println("GC Start Work")
c.DeleteExpired()
case <- c.stopGc:
ticker.Stop()
return
}
}
}
//删除缓存数据项
func (c *Cache) delete(k string) {
delete(c.items, k)
}
//删除过期数据项
func (c *Cache) DeleteExpired() {
now := time.Now().UnixNano()
c.mu.Lock()
defer c.mu.Unlock()
//循环遍历删除 过期的键值对
for k,v := range c.items{
if v.Expiration > 0 && now > v.Expiration{
c.delete(k)
}
}
}
// 设置数据项, 没有锁操作
func (c *Cache) set(k string, v interface{}, d time.Duration) {
var e int64
if d == DefaultExpiration {
d = c.defaultExpiration
}
if d > 0 {
e = time.Now().Add(d).UnixNano()
}
c.items[k] = Item{
Object: v,
Expiration: e,
}
}
func (c *Cache) Set(k string,v interface{},d time.Duration){
var e int64
//如果是 0 表示 使用默认过期时间
if d == DefaultExpiration {
//设置为 默认过期时间
d = c.defaultExpiration
}
if d > 0 {
//计算当前时间 加上 过期时间
e = time.Now().Add(d).UnixNano()
}
c.mu.Lock()
defer c.mu.Unlock()
c.items[k] = Item{
Object:v,
Expiration:e,
}
}
func (c *Cache) get(k string) (interface{} ,bool){
//从hash表中取出value
item,found := c.items[k]
if !found {
return nil,false
}
//取出的对象有没有过期
if item.Expired() {
return nil,false
}
return item.Object,true
}
func (c *Cache) Add(k string,v interface{},d time.Duration) error{
c.mu.Lock()
//如果已存在 无法添加
if _,found := c.get(k);found {
c.mu.Unlock()
return fmt.Errorf("Item %s already exists",k)
}
c.set(k, v, d)
c.mu.Unlock()
return nil
}
//获取value 如果不存在 返回nil false
func (c *Cache) Get(k string)(interface{},bool){
c.mu.RLock()
defer c.mu.RUnlock()
item,found := c.items[k]
if !found {
return nil,false
}
if item.Expired() {
return nil,false
}
return item.Object,true
}
func (c *Cache) Replace(k string,v interface{},d time.Duration) error{
c.mu.Lock()
_,found := c.get(k)
if !found {
fmt.Errorf("Item %s doesn's exist",k)
}
c.set(k,v,d)
c.mu.Unlock()
return nil
}
func (c *Cache) Delete(k string){
c.mu.Lock()
c.delete(k)
c.mu.Unlock()
}
func (c *Cache) Save(w io.Writer) (err error){
enc := gob.NewEncoder(w)
defer func(){
if x := recover();x != nil{
err = fmt.Errorf("Error registring item type with gob library!")
}
}()
c.mu.RLock()
defer c.mu.RUnlock()
for _, v := range c.items{
gob.Register(v.Object)
}
err = enc.Encode(&c.items)
return
}
//保存文本到文件中
func (c *Cache) SaveToFile(file string) error{
f,err := os.Create(file)
if err != nil{
return err
}
if err = c.Save(f) ;err != nil{
f.Close()
return err
}
return f.Close()
}
//从 io.Reader 中读取数据
func (c *Cache) Load(r io.Reader) error{
dec := gob.NewDecoder(r)
items := map[string]Item{}
err := dec.Decode(&items)
if err == nil{
c.mu.Lock()
defer c.mu.Unlock()
for k,v := range items{
ov,found := c.items[k]
if !found || ov.Expired() {
c.items[k] = v
}
}
}
return err
}
//文件中加载数据
func (c *Cache) LoadFile(file string) error{
f,err := os.Open(file)
if err != nil{
return err
}
if err = c.Load(f);err != nil{
f.Close()
return err
}
return f.Close()
}
//返回缓存数据项的数量
func (c *Cache) Count() int{
c.mu.RLock()
defer c.mu.RUnlock()
return len(c.items)
}
//清空缓存
func (c *Cache) Flush() {
c.mu.Lock()
defer c.mu.Unlock()
c.items = map[string]Item{}
}
//停止过期缓存清理
func (c *Cache) StopGc(){
c.stopGc <- true
}
func (c *Cache) Increment(key string,num int){
if v,ok := c.Get(key);ok{
//取interface类型
if val, ok := v.(int); ok {
val = val + num
c.Replace(key,val,0)
}
}
}
//创建缓存系统
func NewCache(defaulteExpiration,gcInterval time.Duration) *Cache{
c:= &Cache{
defaultExpiration: ObjectConfig.DefaulteExpiration,
items: make(map[string]Item,ObjectConfig.MaxCacheLen),
gcInterval: ObjectConfig.GCInterval,
stopGc: make(chan bool),
}
if defaulteExpiration != 0 {
c.defaultExpiration = defaulteExpiration
}
if gcInterval != 0{
c.gcInterval =gcInterval
}
//开启gc
go c.gcLoop()
return c
}