Golang解决缓存miss后的刷新缓存带来的数据库读取压力

Kyli · · 1209 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

工作中碰到缓存失败时,数据库的惊群,本来之前用Redis的SETNX来做锁处理,后来想想,既然用golang写了服务,当然可以把锁直接做在内存里,就自己写了一小段代码。

package resourceslock

import (
	"errors"
	"sync"
)

var (
	Lock resourcesLock = resourcesLock{
		lockerMapMtx:     new(sync.Mutex),
		lockerMap:        make(map[ResourceTopic]chan void),
		topicWaitlistMtx: make(map[ResourceTopic]*sync.Mutex),
		topicWaitlist:    make(map[ResourceTopic][]chan bool),
	} // universal lock
	voidMsg void = void{}
)

type ResourceTopic string
type ProtectCondition func() bool
type ReloadFunc func() (interface{}, error)
type void struct{}
type resourcesLock struct {
	lockerMapMtx     *sync.Mutex
	topicWaitlistMtx map[ResourceTopic]*sync.Mutex
	lockerMap        map[ResourceTopic](chan void)
	topicWaitlist    map[ResourceTopic][](chan bool)
}

func (self *resourcesLock) Protect(topic ResourceTopic, cond *ProtectCondition, reload *ReloadFunc) error {
	if (*cond)() == true {
		return nil
	}

	var err error
	self.createLockChn(topic)

	select {
	case <-self.lockerMap[topic]:
		_, err = (*reload)()
		if err != nil {
			self.notifyError(topic)
		} else {
			self.notifySuccess(topic)
		}
		self.lockerMap[topic] <- voidMsg
	default:
		if !<-self.addToWaitlist(topic) {
			err = errors.New("Resource is not refreshed.")
		}
	}

	return err
}

func (self *resourcesLock) createLockChn(topic ResourceTopic) {
	self.lockerMapMtx.Lock()
	_, ok := self.lockerMap[topic]
	if !ok {
		ch := make(chan void, 1)
		ch <- voidMsg
		self.lockerMap[topic] = ch
		self.topicWaitlistMtx[topic] = new(sync.Mutex)
	}
	self.lockerMapMtx.Unlock()
}

func (self *resourcesLock) addToWaitlist(topic ResourceTopic) <-chan bool {
	ch := make(chan bool)
	self.topicWaitlistMtx[topic].Lock()
	self.topicWaitlist[topic] = append(self.topicWaitlist[topic], ch)
	self.topicWaitlistMtx[topic].Unlock()

	return ch
}

func (self *resourcesLock) notifyError(topic ResourceTopic) {
	self.notify(topic, false)
}

func (self *resourcesLock) notifySuccess(topic ResourceTopic) {
	self.notify(topic, true)
}

func (self *resourcesLock) notify(topic ResourceTopic, suc bool) {
	self.topicWaitlistMtx[topic].Lock()
	for i, c := range self.topicWaitlist[topic] {
		if suc {
			c <- true
		} else {
			c <- false
		}
		self.topicWaitlist[topic][i] = nil
	}
	self.topicWaitlist[topic] = self.topicWaitlist[topic][:0]
	self.topicWaitlistMtx[topic].Unlock()
}

 


有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1209 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传