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


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

查看所有标签

猜你喜欢:

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

Python语言程序设计

Python语言程序设计

[美]梁勇(Lang Y. D.) / 李娜 / 机械工业出版社 / 2015-4 / 79.00元

本书采用“问题驱动”、“基础先行”和“实例和实践相结合”的方式,讲述如何使用Python语言进行程序设计。本书首先介绍Python程序设计的基本概念,接着介绍面向对象程序设计方法,最后介绍算法与数据结构方面的内容。为了帮助学生更好地掌握相关知识,本书每章都包括以下模块:学习目标,引言,关键点,检查点,问题,本章总结,测试题,编程题,注意、提示和警告。 本书可以作为高等院校计算机及相关专业Py......一起来看看 《Python语言程序设计》 这本书的介绍吧!

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

在线压缩/解压 CSS 代码

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码