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


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

查看所有标签

猜你喜欢:

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

Web Design Index 7

Web Design Index 7

Pepin Press / PEPIN PRESS / 20070501 / TWD$1000.00

《網頁設計索引》年刊自2000年誕生起現已發展成同行業最重要的出版物之一,每年都會對網頁設計的最新趨勢給予準確概述。網站可簡單到只有一頁,也可以設計為具有最新數位性能的複雜結構。《網頁設計索引》的篩選標準是根據設計品質、創意及效率-而不管複雜程度如何。因此在本書中你可以找到所有可能的樣式和風格的實例。 每輯《網頁設計索引》都展示了1002個精采的網頁 同時提供了每個網頁的URL。網頁設計和編......一起来看看 《Web Design Index 7》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

URL 编码/解码
URL 编码/解码

URL 编码/解码

SHA 加密
SHA 加密

SHA 加密工具