资讯专栏INFORMATION COLUMN

golang防缓存击穿利器--singleflight

keithyau / 1421人阅读

摘要:缓存击穿给缓存加一个过期时间,下次未命中缓存时再去从数据源获取结果写入新的缓存,这个是后端开发人员再熟悉不过的基操。

缓存击穿

    给缓存加一个过期时间,下次未命中缓存时再去从数据源获取结果写入新的缓存,这个是后端开发人员再熟悉不过的基操。本人之前在做直播平台活动业务的时候,当时带着这份再熟练不过的自信,把复杂的数据库链表语句写好,各种微服务之间调用捞数据最后算好的结果,丢进了缓存然后设了一个过期时间,当时噼里啪啦两下写完代码觉得稳如铁蛋,结果在活动快结束之前,数据库很友好的挂掉了。当时回去查看监控后发现,是在活动快结束前,大量用户都在疯狂的刷活动页,导致缓存过期的瞬间有大量未命中缓存的请求直接打到数据库上所导致的,所以这个经典的问题稍不注意还是害死人

    防缓存击穿的方式有很多种,比如通过计划任务来跟新缓存使得从前端过来的所有请求都是从缓存读取等等。之前读过 groupCache的源码,发现里面有一个很有意思的库,叫singleFlight, 因为groupCache从节点上获取缓存如果未命中,则会去其他节点寻找,其他节点还没有的话再从数据源获取,所以这个步骤对于防击穿非常有必要。singleFlight使得groupCache在多个并发请求对一个失效的key进行源数据获取时,只让其中一个得到执行,其余阻塞等待到执行的那个请求完成后,将结果传递给阻塞的其他请求达到防止击穿的效果。

SingleFlight 使用Demo

本文模拟一个数据源是从调用rpc获取的场景

然后再模拟一百个并发请求在缓存失效的瞬间同时调用rpc访问源数据

效果

可以看到100个并发请求从源数据获取时,rpcServer端只收到了来自client 17的请求,而其余99个最后也都得到了正确的返回值。

SingleFlight 源码剖析

在看完singleFlight的实际效果后,欣喜若狂,想必其实现应该相当复杂吧, 结果翻看源码一看, 100行不到的代码就解决了这么个业务痛点, 不得不佩服。

package singlefilght

import "sync"

type Group struct {
    mu sync.Mutex
    m map[string]*Call // 对于每一个需要获取的key有一个对应的call
}

// call代表需要被执行的函数
type Call struct {
    wg sync.WaitGroup // 用于阻塞这个调用call的其他请求
    val interface{} // 函数执行后的结果
    err error         // 函数执行后的error
}

func (g *Group) Do(key string, fn func()(interface{}, error)) (interface{}, error) {

    g.mu.Lock()
    if g.m == nil {
        g.m = make(map[string]*Call)
    }
    
    // 如果获取当前key的函数正在被执行,则阻塞等待执行中的,等待其执行完毕后获取它的执行结果
    if c, ok := g.m[key]; ok {
        g.mu.Unlock()
        c.wg.Wait()
        return c.val, c.err
    }

    // 初始化一个call,往map中写后就解
    c := new(Call)
    c.wg.Add(1)
    g.m[key] = c
    g.mu.Unlock()
    
  // 执行获取key的函数,并将结果赋值给这个Call
    c.val, c.err = fn()
    c.wg.Done()
    
    // 重新上锁删除key
    g.mu.Lock()
    delete(g.m, key)
    g.mu.Unlock()

    return c.val, c.err

}

    对的没看错, 就这么100行不到的代码就能解决缓存击穿的问题,这算是我写过最愉快的一篇博了,同时也推荐大家去读一读groupCache这个项目的源码,会有更多惊喜的发现

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/62074.html

相关文章

  • 雪崩利器:熔断器 Hystrix 的原理与使用

    摘要:前言分布式系统中经常会出现某个基础服务不可用造成整个系统不可用的情况这种现象被称为服务雪崩效应为了应对服务雪崩一种常见的做法是手动服务降级而的出现给我们提供了另一种选择服务雪崩效应的定义服务雪崩效应是一种因服务提供者的不可用导致服务调用者的 前言 分布式系统中经常会出现某个基础服务不可用造成整个系统不可用的情况, 这种现象被称为服务雪崩效应. 为了应对服务雪崩, 一种常见的做法是手动服...

    jayzou 评论0 收藏0
  • 雪崩利器:熔断器 Hystrix 的原理与使用

    摘要:前言分布式系统中经常会出现某个基础服务不可用造成整个系统不可用的情况这种现象被称为服务雪崩效应为了应对服务雪崩一种常见的做法是手动服务降级而的出现给我们提供了另一种选择服务雪崩效应的定义服务雪崩效应是一种因服务提供者的不可用导致服务调用者的 前言 分布式系统中经常会出现某个基础服务不可用造成整个系统不可用的情况, 这种现象被称为服务雪崩效应. 为了应对服务雪崩, 一种常见的做法是手动服...

    JessYanCoding 评论0 收藏0
  • 缓存穿透,缓存击穿缓存雪崩解决方案分析

    摘要:解决方案通过布隆过滤器拦截。对空结果进行缓存,但是过期时间很短,不超过分钟。缓存雪崩介绍缓存雪崩是指设置缓存采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到,瞬间压力过重雪崩。 缓存穿透 介绍 缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时被动写,并且处于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓...

    Soarkey 评论0 收藏0
  • Redis实现广告缓存、并完善缓存击穿

    摘要:完善缓存击穿问题问题描述,分布式场景中,有时候存在高并发访问,比如秒杀活动。在高并发访问下请求全部怼到数据库,可能导致数据库挂掉,这就是缓存击穿。缓存击穿解决方案已经能解决日常情况,但还是有一定提升的空间的。 做人、做程序,变则通,不变只能一直死循环下去 ————尼古斯拉 Docker安装官方Redis 参考文章:Docker安装官方Redis镜像并启用密码认证 拉取最新版...

    KitorinZero 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<