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稳定性


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

查看所有标签

猜你喜欢:

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

垃圾回收算法手册:自动内存管理的艺术

垃圾回收算法手册:自动内存管理的艺术

Richard Jones、Eliot Moss、Antony Hosking / 王雅光、薛迪 / 机械工业出版社 / 2016-3 / 139

在自动内存管理领域,Richard Jones于1996年出版的《Garbage Collection:Algorithms for Automatic Dynamic Memory Management》可谓是一部里程碑式的作品。接近20年过去了,垃圾回收技术得到了非常大的发展,因此有必要将该领域当前最先进的技术呈现给读者。本书汇集了自动内存管理研究者和开发者们在过去50年间的丰富经验,在本书中......一起来看看 《垃圾回收算法手册:自动内存管理的艺术》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

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

各进制数互转换器

随机密码生成器
随机密码生成器

多种字符组合密码