golang sync.singleflight 解决热点缓存穿透问题
在 go 的 sync 包中,有一个 singleflight 包,里面有一个 singleflight.go 文件,代码加注释,一共 200 行出头。内容包括以下几块儿:
Group结构体管理一组相关的函数调用工作,它包含一个互斥锁和一个map,map的key是函数的名称,value是对应的call结构体。call结构体表示一个inflight或已完成的函数调用,包含等待组件WaitGroup、调用结果val和err、调用次数dups和通知通道chans。Do方法接收一个key和函数fn,它会先查看map中是否已经有这个key的调用在inflight,如果有则等待并返回已有结果,如果没有则新建一个call并执行函数调用。DoChan类似Do但返回一个channel来接收结果。doCall方法包含了具体处理调用的逻辑,它会在函数调用前后添加defer来recoverpanic和区分正常return与runtime.Goexit。- 如果发生
panic,会将panicwraps成错误返回给等待的channel,如果是goexit会直接退出。正常return时会将结果发送到所有通知channel。 Forget方法可以忘记一个key的调用,下次Do时会重新执行函数。
这个包通过互斥锁和 map 实现了对相同 key 的函数调用去重,可以避免对已有调用的重复计算,同时通过 channel 机制可以通知调用者函数执行结果。在一些需要确保单次执行的场景中,可以使用这个包中的方法。
通过 singleflight 可以很容易实现缓存和去重的效果,避免重复计算,接下来,我们来模拟一下并发请求可能导致的缓存穿透场景,以及如何用 singleflight 包来解决这个问题:
package mainimport ("context""fmt""golang.org/x/sync/singleflight""sync/atomic""time")type Result string
// 模拟查询数据库
func find(ctx context.Context, query string) (Result, error) {return Result(fmt.Sprintf("result for %q", query)), nil
}func main() {var g singleflight.Groupconst n = 200waited := int32(n)done := make(chan struct{})key := "this is key"for i := 0; i < n; i++ {go func(j int) {v, _, shared := g.Do(key, func() (interface{}, error) {ret, err := find(context.Background(), key)return ret, err})if atomic.AddInt32(&waited, -1) == 0 {close(done)}fmt.Printf("index: %d, val: %v, shared: %v\n", j, v, shared)}(i)}select {case <-done:case <-time.After(time.Second):fmt.Println("Do hangs")}time.Sleep(time.Second * 4)
}
在这段程序中,如果重复使用查询结果,shared 会返回 true,穿透查询会返回 false
上面的设计中还有一个问题,就是在 Do 阻塞时,所有请求都会阻塞,内存可能会出现大的问题。
此时,Do 可以更换为DoChan,两者实现上完全一样,不同的是,DoChan() 通过 channel 返回结果。因此可以使用 select 语句实现超时控制
ch := g.DoChan(key, func() (interface{}, error) {ret, err := find(context.Background(), key)return ret, err
})
// Create our timeout
timeout := time.After(500 * time.Millisecond)var ret singleflight.Result
select {
case <-timeout: // Timeout elapsedfmt.Println("Timeout")return
case ret = <-ch: // Received result from channelfmt.Printf("index: %d, val: %v, shared: %v\n", j, ret.Val, ret.Shared)
}
在超时时主动返回,不阻塞。
此时又引入了另一个问题,这样的每一次的请求,并不是高可用的,成功率是无法保证的。这时候可以增加一定的请求饱和度来保证业务的最终成功率,此时一次请求还是多次请求,对于下游服务而言并没有太大区别,此时使用 singleflight 只是为了降低请求的数量级,那么可以使用 Forget() 来提高下游请求的并发。
ch := g.DoChan(key, func() (interface{}, error) {go func() {time.Sleep(10 * time.Millisecond)fmt.Printf("Deleting key: %v\n", key)g.Forget(key)}()ret, err := find(context.Background(), key)return ret, err
})
当然,这种做法依然无法保证100%的成功,如果单次的失败无法容忍,在高并发的场景下需要使用更好的处理方案,比如牺牲一部分实时性、完全使用缓存查询 + 异步更新等。
相关文章:
golang sync.singleflight 解决热点缓存穿透问题
在 go 的 sync 包中,有一个 singleflight 包,里面有一个 singleflight.go 文件,代码加注释,一共 200 行出头。内容包括以下几块儿: Group 结构体管理一组相关的函数调用工作,它包含一个互斥锁和一个 map,map 的 key 是…...
4、Linux驱动开发:设备-设备号设备号注册
目录 🍅点击这里查看所有博文 随着自己工作的进行,接触到的技术栈也越来越多。给我一个很直观的感受就是,某一项技术/经验在刚开始接触的时候都记得很清楚。往往过了几个月都会忘记的差不多了,只有经常会用到的东西才有可能真正记…...
C++(MFC)调用Python
环境: phyton版本:3.10 VS版本:VS2017 包含文件头:Python\Python310\include 包含库文件:Python\Python310\libs 程序运行期间,以下函数只需要调用一次即可,重复调用会导致崩溃 void Initial…...
深度学习实践——循环神经网络实践
系列实验 深度学习实践——卷积神经网络实践:裂缝识别 深度学习实践——循环神经网络实践 深度学习实践——模型部署优化实践 深度学习实践——模型推理优化练习 代码可见于: 深度学习实践——循环神经网络实践 0 概况1 架构实现1.1 RNN架构1.1.1 RNN架…...
docker简单web管理docker.io/uifd/ui-for-docker
要先pull这个镜像docker.io/uifd/ui-for-docker 这个软件默认只能使用9000端口,别的不行,因为作者在镜像制作时已加入这一层 刚下下来镜像可以通过docker history docker.io/uifd/ui-for-docker 查看到这个端口已被 设置 如果在没有设置br0网关时&…...
SpringBoot内嵌的Tomcat:
SpringBoot内嵌Tomcat源码: 1、调用启动类SpringbootdemoApplication中的SpringApplication.run()方法。 SpringBootApplication public class SpringbootdemoApplication {public static void main(String[] args) {SpringApplication.run(SpringbootdemoApplicat…...
企业级docker应用注意事项
现在很多企业使用容器化技术部署应用,绕不开的docker技术,在生产环境docker常用操作总结。参考:https://juejin.cn/post/7259275893796651069 1. 尽可能使用官方镜像 在docker hub 官方 使用后面带有 DOCKER OFFICIAL IMAGE 标签的镜像&…...
腾讯云高性能计算集群CPU服务器处理器说明
腾讯云高性能计算集群以裸金属云服务器为节点,通过RDMA互联,提供了高带宽和极低延迟的网络服务,能满足大规模高性能计算、人工智能、大数据推荐等应用的并行计算需求,腾讯云服务器网分享腾讯云服务器高性能计算集群CPU处理器说明&…...
tinkerCAD案例:23.Tinkercad 中的自定义字体
tinkerCAD案例:23.Tinkercad 中的自定义字体 原文 Tinkercad Projects Tinkercad has a fun shape in the Shape Generators section that allows you to upload your own font in SVG format and use it in your designs. I’ve used it for a variety of desi…...
Box-Cox 变换
Box-cox 变化公式如下: y ( λ ) { y λ − 1 λ λ ≠ 0 l n ( y ) λ 0 y^{(\lambda)}\left\{ \begin{aligned} \frac{y^{\lambda} - 1}{\lambda} && \lambda \ne 0 \\ ln(y) && \lambda 0 \end{aligned} \right. y(λ)⎩ ⎨ ⎧λyλ−1ln…...
Linux wc命令用于统计文件的行数,字符数,字节数
Linux wc命令用于计算字数。 利用wc指令我们可以计算文件的Byte数、字数、或是列数,若不指定文件名称、或是所给予的文件名为"-",则wc指令会从标准输入设备读取数据。 语法 wc [-clw][–help][–version][文件…] 参数: -c或–b…...
Python读取多个栅格文件并提取像元的各波段时间序列数据与变化值
本文介绍基于Python语言,读取文件夹下大量栅格遥感影像文件,并基于给定的一个像元,提取该像元对应的全部遥感影像文件中,指定多个波段的数值;修改其中不在给定范围内的异常值,并计算像元数值在每一景遥感影…...
Linux 之 wget curl
wget 命令 wget是非交互式的文件下载器,可以在命令行内下载网络文件 语法: wget [-b] url 选项: -b ,可选,background 后台下载,会将日志写入到 当前工作目录的wget-log文件 参数 url : 下载链…...
AngularJS 和 React区别
目录 1. 背景:2. 版本:3. 应用场景:4. 语法:5. 优缺点:6. 代码示例: AngularJS 和 React 是两个目前最为流行的前端框架之一。它们有一些共同点,例如都是基于 JavaScript 的开源框架,…...
【Solr】Solr搜索引擎使用
文章目录 一、什么是Solr?二 、数据库本身就支持搜索啊,干嘛还要搞个什么solr?三、如果我们想要使用solr那么首先我们得安装它 一、什么是Solr? 其实我们大多数人都使用过Solr,也许你不会相信我说的这句话,但是事实却是如此啊 ! 每当你想买自己喜欢的东东时,你可能会打开某…...
一起学算法(选择排序篇)
距离上次更新已经很久了,以前都是非常认真的写笔记进行知识分享,但是带来的情况并不是很好,一度认为发博客是没有意义的,但是这几天想了很多,已经失去了当时写博客的初心了,但是我觉得应该做点有意义的事&a…...
智能体的主观和能动
摘要 智能体的主动性是提升智能机器的能力的关键。围绕智能体的主动性存在很多思想迷雾,本文继续我们以前的工作,试图清理这些概念上的问题。我们的讨论显示:要研究主动性,并不一定需要研究意识,仅需要研究主观和能动就…...
AB 压力测试
服务器配置 阿里云Ubuntu 64位 CPU1 核 内存2 GB 公网带宽1 Mbps ab -c100 -n1000 http://127.0.0.1:9501/ -n:在测试会话中所执行的请求个数。默认时,仅执行一个请求。 -c:一次产生的请求个数。默认是一次一个。 ab -c 100 -n 200 ht…...
多旋翼物流无人机节能轨迹规划(Python代码实现)
目录 💥1 概述 📚2 运行结果 🌈3 Python代码实现 🎉4 参考文献 💥1 概述 多旋翼物流无人机的节能轨迹规划是一项重要的技术,可以有效减少无人机的能量消耗,延长飞行时间,提高物流效率…...
Vue通过指令 命令将打包好的dist静态文件上传到腾讯云存储桶 (保存原有存储目录结构)
1、在项目根目录创建uploadToCOS.js文件 (建议起简单的名字 方便以后上传输入命令方便) 2、uploadToCOS.js文件代码编写 const path require(path); const fs require(fs); const COS require(cos-nodejs-sdk-v5);// 配置腾讯云COS参数 const cos n…...
日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻
在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...
python/java环境配置
环境变量放一起 python: 1.首先下载Python Python下载地址:Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个,然后自定义,全选 可以把前4个选上 3.环境配置 1)搜高级系统设置 2…...
生成 Git SSH 证书
🔑 1. 生成 SSH 密钥对 在终端(Windows 使用 Git Bash,Mac/Linux 使用 Terminal)执行命令: ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" 参数说明: -t rsa&#x…...
Linux-07 ubuntu 的 chrome 启动不了
文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了,报错如下四、启动不了,解决如下 总结 问题原因 在应用中可以看到chrome,但是打不开(说明:原来的ubuntu系统出问题了,这个是备用的硬盘&a…...
C++.OpenGL (10/64)基础光照(Basic Lighting)
基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...
Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...
GruntJS-前端自动化任务运行器从入门到实战
Grunt 完全指南:从入门到实战 一、Grunt 是什么? Grunt是一个基于 Node.js 的前端自动化任务运行器,主要用于自动化执行项目开发中重复性高的任务,例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...
Web中间件--tomcat学习
Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机,它可以执行Java字节码。Java虚拟机是Java平台的一部分,Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...
C++ 设计模式 《小明的奶茶加料风波》
👨🎓 模式名称:装饰器模式(Decorator Pattern) 👦 小明最近上线了校园奶茶配送功能,业务火爆,大家都在加料: 有的同学要加波霸 🟤,有的要加椰果…...
【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)
LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 题目描述解题思路Java代码 题目描述 题目链接:LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...
