go防止缓存穿透

栏目: IT技术 · 发布时间: 4年前

内容简介:下面是一个简单的示例我们的实际项目中有

当线上接口请求量比较大时,如果恰好遇到缓存失效,会造成大量的请求直接打到数据库,导致数据库压力过大、甚至崩溃。如果缓存的数据实时性要求不那么高,可以试试 do-once-while-concurrent

https://github.com/abusizhish...

do-once-while-concurrent 中有三个主要方法,

Req 方法
Wait 方法
Release 方法

下面是一个简单的示例

我们的实际项目中有 两级缓存 ,一级 本地缓存 ,一级 redis ,如果都查询不到才会 读取mysql调用中台接口 ,本次只模拟 本地缓存失效 时, do-once-while-concurrent 对防止 缓存穿透 的处理(实际叫 重复资源过滤 更合理)

1.缓存失效时, 所有请求该缓存的请求会先调用 Req方法 对具有相同标签的重复请求进行拦截

2.只有第一个请求会 获取锁 ,执行读取 redis 操作

3.所有其他的线程 获取锁 失败,调用 Wait 方法,等待第一个线程 执行结束

4.第一个线程读取到用户信息,写入本地缓存,通过 close(chan) 事件来 广播消息

5.其他线程收到消息,结束 等待 ,读取本地缓存,返回用户信息

package main  
  
import (  
   "errors"  
   "fmt" 
   "github.com/abusizhishen/do-once-while-concurrent/src" 
   "log" 
   "sync" 
   "time"
)  
  
func main() {  
   //并发do something  
   for i := 0; i < 5; i++ {  
      go doSomeThing()  
   }  
  
   //避免程序直接退出
   time.Sleep(time.Second * 5)  
}  
  
var once src.DoOnce  
  
//模拟获取用户信息  
func doSomeThing() {  
   var userId = 12345  
   var user, err = getUserInfo(userId)  
   fmt.Println(user, err)  
}  
  
//example for usage  
// 演示获取用户详情的过程,先从本地缓存读取用户,如果本地缓存不存在,就从redis读取  
var keyUser = "user_%d"  
  
func getUserInfo(userId int) (user UserInfo, err error) {  
   user, err = userCache.GetUser(userId)  
   if err == nil {  
      return  
  }  
  
   log.Println(err)  
   var requestTag = fmt.Sprintf(keyUser, userId)  
   if !once.Req(requestTag) {  
      log.Println("没抢到锁,等待抢到锁的线程执行结束。。。")  
      once.Wait(requestTag)  
      log.Println("等待结束:", requestTag)  
      return userCache.GetUser(userId)  
   }  
  
   //得到资源后释放锁  
   defer once.Release(requestTag)  
   log.Println(requestTag, "获得锁,let's Go")  
  
   //为演示效果,sleep  
  time.Sleep(time.Second * 3)  
  
   //redis读取用户信息  
  log.Println("redis读取用户信息:", userId)  
  user, err = getUserInfoFromRedis(userId)  
  if err != nil {  
     return  
  }  
  
   //用户写入缓存  
  log.Println("用户写入缓存:", userId)  
  userCache.setUser(user)  
  return  
}  
  
//用户信息缓存  
type UserCache struct {  
   Users map[int]UserInfo  
   sync.RWMutex  
}  
  
type UserInfo struct {  
  Id   int  
  Name string  
  Age  int  
}  
  
var userCache UserCache  
var errUserNotFound = errors.New("user not found in cache")  
  
func (c *UserCache) GetUser(id int) (user UserInfo, err error) {  
   c.RLock()  
   defer c.RUnlock()  
   var ok bool  
   user, ok = userCache.Users[id]  
   if ok {  
      return  
   }  
  
   return user, errUserNotFound  
}  
  
func (c *UserCache) setUser(user UserInfo) {  
   c.Lock()  
   defer c.Unlock()  
   if c.Users == nil {  
      c.Users = make(map[int]UserInfo)  
   }  
  
   c.Users[user.Id] = user  
   return  
}  
  
func getUserInfoFromRedis(id int) (user UserInfo, err error) {  
   user = UserInfo{  
      Id:   12345,  
      Name: "abusizhishen",  
      Age:  18,  
  }  
   return  
}

输出

2020/03/09 20:11:39 user not found in cache
2020/03/09 20:11:39 user_12345 获得锁,let's Go
2020/03/09 20:11:39 user not found in cache
2020/03/09 20:11:39 没抢到锁,等待抢到锁的线程执行结束。。。
2020/03/09 20:11:39 user not found in cache
2020/03/09 20:11:39 没抢到锁,等待抢到锁的线程执行结束。。。
2020/03/09 20:11:39 user not found in cache
2020/03/09 20:11:39 user not found in cache
2020/03/09 20:11:39 没抢到锁,等待抢到锁的线程执行结束。。。
2020/03/09 20:11:39 没抢到锁,等待抢到锁的线程执行结束。。。
2020/03/09 20:11:42 redis读取用户信息: 12345
2020/03/09 20:11:42 用户写入缓存: 12345
2020/03/09 20:11:42 等待结束: user_12345
2020/03/09 20:11:42 等待结束: user_12345
{12345 abusizhishen 18} <nil>
{12345 abusizhishen 18} <nil>
{12345 abusizhishen 18} <nil>
2020/03/09 20:11:42 等待结束: user_12345
{12345 abusizhishen 18} <nil>
2020/03/09 20:11:42 等待结束: user_12345
{12345 abusizhishen 18} <nil>

可以看到,当第一个线程 获取锁 后,其他线程全部处于 等待状态 ,直到第一个线程 执行结果释放锁 ,其他线程 获取到数据 ,返回结果

事实上不止于防止 缓存穿透 , do-once-while-concurrent 更准确的定位是 重复资源过滤 ,,在某讲座业务中,使用 do-once-while-concurrent 来避免同一时刻同一用户id 重复解析 、列表页 重复检索排序 等,减少了资源竞争,提高了整体的 qps稳定性


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

大连接

大连接

[美] 尼古拉斯•克里斯塔基斯(Nicholas A. Christakis)、[美] 詹姆斯•富勒(James H. Fowler) / 简学 / 中国人民大学出版社 / 2013-1 / 59.90元

[内容简介] 1. 本书是继《六度分隔》之后,社会科学领域最重要的作品。作者发现:相距三度之内是强连接,强连接可以引发行为;相聚超过三度是弱连接,弱连接只能传递信息。 2. 本书讲述了社会网络是如何形成的以及对人类现实行为的影响,如对人类的情绪、亲密关系、健康、经济的运行和政治的影响等,并特别指出,三度影响力(即朋友的朋友的朋友也能影响到你)是社会化网络的强连接原则,决定着社会化网络的......一起来看看 《大连接》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具