40分钟学 Go 语言高并发实战:高性能缓存组件开发

高性能缓存组件开发

学习要点:

  1. 缓存淘汰策略
  2. 并发安全设计
  3. 性能优化
  4. 监控统计

一、缓存淘汰策略

缓存作为性能优化的常用手段,如何选择合适的缓存淘汰策略是关键。常见的缓存淘汰策略有以下几种:

策略特点
FIFO (First In First Out)先进先出,淘汰最早添加的缓存项
LRU (Least Recently Used)淘汰最近最少使用的缓存项
LFU (Least Frequently Used)淘汰访问频率最低的缓存项
TTL (Time To Live)根据缓存项的过期时间淘汰,缓存项在指定时间内未被访问则被淘汰

以下是Go语言实现LRU缓存淘汰策略的示例代码:

import ("container/list""sync"
)type LRUCache struct {capacity intmu       sync.Mutexcache    map[string]*list.Elementlist     *list.List
}type cacheNode struct {key   stringvalue interface{}
}func NewLRUCache(capacity int) *LRUCache {return &LRUCache{capacity: capacity,cache:    make(map[string]*list.Element),list:     list.New(),}
}func (c *LRUCache) Get(key string) (value interface{}, ok bool) {c.mu.Lock()defer c.mu.Unlock()if elem, ok := c.cache[key]; ok {c.list.MoveToFront(elem)return elem.Value.(*cacheNode).value, true}return nil, false
}func (c *LRUCache) Put(key string, value interface{}) {c.mu.Lock()defer c.mu.Unlock()if elem, ok := c.cache[key]; ok {c.list.MoveToFront(elem)elem.Value.(*cacheNode).value = valuereturn}if c.list.Len() == c.capacity {last := c.list.Back()c.list.Remove(last)delete(c.cache, last.Value.(*cacheNode).key)}node := &cacheNode{key: key, value: value}elem := c.list.PushFront(node)c.cache[key] = elem
}

在这个实现中,我们使用一个list.List来维护缓存项的访问顺序,并在map中存储缓存项的key-value对。当Get()一个缓存项时,我们将其移动到链表的最前面;当Put()一个新的缓存项时,如果缓存已满,我们会淘汰最近最少使用的缓存项。这样既可以实现LRU策略,又可以保证常数时间的访问和淘汰操作。

二、并发安全设计

缓存作为热点数据存取的中间层,需要考虑并发安全的设计。常见的并发安全措施有:

  1. 互斥锁: 使用sync.Mutexsync.RWMutex保护缓存的读写操作。
  2. 原子操作: 使用sync/atomic包提供的原子操作,如atomic.LoadInt64()atomic.StoreInt64()等。
  3. 无锁并发: 利用无锁并发数据结构,如sync.Map实现无锁的并发读写。

下面是一个使用sync.RWMutex实现并发安全缓存的示例:

type SafeCache struct {mu sync.RWMutexm  map[string]interface{}
}func NewSafeCache() *SafeCache {return &SafeCache{m: make(map[string]interface{}),}
}func (c *SafeCache) Get(key string) (value interface{}, ok bool) {c.mu.RLock()defer c.mu.RUnlock()value, ok = c.m[key]return
}func (c *SafeCache) Put(key string, value interface{}) {c.mu.Lock()defer c.mu.Unlock()c.m[key] = value
}

在这个实现中,我们使用sync.RWMutex来保护缓存的读写操作。当调用Get()方法时,我们使用RLock()获取读锁;当调用Put()方法时,我们使用Lock()获取写锁。这样可以确保多个goroutine并发访问缓存时不会出现数据竞争问题。

三、性能优化

缓存作为性能优化的重要手段,其自身的性能也需要优化。常见的性能优化手段有:

  1. 使用无锁并发数据结构: 如前面提到的sync.Map,可以在不使用锁的情况下实现并发安全的缓存。
  2. 批量操作: 对于大量的缓存操作,可以采用批量操作的方式,减少锁竞争和系统调用的开销。
  3. 分段缓存: 将缓存划分为多个独立的部分,每个部分使用独立的锁,从而提高并发度。
  4. 异步刷新: 对于需要定期更新的缓存,可以采用异步刷新的方式,减少对前端请求的阻塞。
  5. 多级缓存: 结合内存缓存和磁盘缓存,实现多级缓存架构,提高命中率和容量。

下面是一个使用分段缓存和异步刷新实现的高性能缓存组件:

type SegmentedCache struct {segments []*SafeCacherefresh  chan string
}func NewSegmentedCache(numSegments int) *SegmentedCache {c := &SegmentedCache{segments: make([]*SafeCache, numSegments),refresh:  make(chan string, 1000),}for i := 0; i < numSegments; i++ {c.segments[i] = NewSafeCache()}go c.refreshLoop()return c
}func (c *SegmentedCache) Get(key string) (value interface{}, ok bool) {segment := c.getSegment(key)return segment.Get(key)
}func (c *SegmentedCache) Put(key string, value interface{}) {segment := c.getSegment(key)segment.Put(key, value)c.refresh <- key
}func (c *SegmentedCache) getSegment(key string) *SafeCache {return c.segments[hash(key)%len(c.segments)]
}func (c *SegmentedCache) refreshLoop() {for key := range c.refresh {// 异步刷新缓存c.refreshCache(key)}
}func (c *SegmentedCache) refreshCache(key string) {// 从数据源获取最新数据,并更新缓存
}

在这个实现中,我们将缓存划分为多个独立的段,每个段使用独立的SafeCache实例。当Get()Put()时,我们根据key的哈希值选择对应的缓存段进行操作。同时,我们使用一个异步的refreshLoop协程,将需要更新的key写入到一个channel中,由refreshLoop协程负责异步刷新缓存。这种分段缓存和异步刷新的方式可以大幅提高缓存的并发性和吞吐量。

四、监控统计

对缓存组件的运行状态进行监控和统计是非常重要的,可以帮助我们及时发现问题,优化性能。常见的监控指标有:

  1. 缓存命中率: 反映缓存的有效性,可以帮助我们调整缓存策略。
  2. 缓存读写次数: 反映缓存的使用频率,可以帮助我们调整缓存容量。
  3. 缓存淘汰次数: 反映缓存压力情况,可以帮助我们调整缓存淘汰策略。
  4. 并发读写次数: 反映缓存的并发性能,可以帮助我们调整并发控制策略。
  5. 缓存延迟: 反映缓存的响应性能,可以帮助我们优化缓存实现。

下面是一个使用Prometheus监控Go语言缓存组件的示例:

import ("github.com/prometheus/client_golang/prometheus""github.com/prometheus/client_golang/prometheus/promauto"
)var (cacheHits = promauto.NewCounter(prometheus.CounterOpts{Name: "cache_hits_total",Help: "Total number of cache hits",})cacheMisses = promauto.NewCounter(prometheus.CounterOpts{Name: "cache_misses_total",Help: "Total number of cache misses",})cacheReads = promauto.NewCounter(prometheus.CounterOpts{Name: "cache_reads_total",Help: "Total number of cache reads",})cacheWrites = promauto.NewCounter(prometheus.CounterOpts{Name: "cache_writes_total",Help: "Total number of cache writes",})cacheEvictions = promauto.NewCounter(prometheus.CounterOpts{Name: "cache_evictions_total",Help: "Total number of cache evictions",})cacheLatency = promauto.NewHistogram(prometheus.HistogramOpts{Name: "cache_latency_seconds",Help: "Cache access latency distribution",})
)type PrometheusCache struct {*SafeCache
}func (c *PrometheusCache) Get(key string) (value interface{}, ok bool) {start := time.Now()value, ok = c.SafeCache.Get(key)cacheLatency.Observe(time.Since(start).Seconds())if ok {cacheHits.Inc()} else {cacheMisses.Inc()}cacheReads.Inc()return
}func (c *PrometheusCache) Put(key string, value interface{}) {start := time.Now()c.SafeCache.Put(key, value)cacheLatency.Observe(time.Since(start).Seconds())cacheWrites.Inc()
}func (c *PrometheusCache) Evict(key string) {c.SafeCache.Evict(key)cacheEvictions.Inc()
}

在这个实现中,我们使用Prometheus客户端库定义了几个监控指标,包括缓存命中率、缓存读写次数、缓存淘汰次数和缓存延迟。在Get()Put()Evict()方法中,我们分别记录这些指标的值。通过Prometheus服务器,我们可以查看这些监控指标的实时数据,并根据数据分析缓存组件的运行状态,进而优化缓存的性能。

综上所述

本节课程我们详细介绍了Go语言高性能缓存组件的设计与实现,包括缓存淘汰策略、并发安全设计、性能优化和监控统计等关键内容。希望通过这些知识和实战演练,能够帮助您构建出功能强大、性能卓越的缓存组件,为您的应用程序带来显著的性能提升。


怎么样今天的内容还满意吗?再次感谢观众老爷的观看,关注GZH:凡人的AI工具箱,回复666,送您价值199的AI大礼包。最后,祝您早日实现财务自由,还请给个赞,谢谢!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/479870.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C#开发合集

用C#轻松搞定m3u8视频下载与合并 嘿&#xff0c;程序员们&#xff01;今天咱们来聊聊如何用C#写个小程序&#xff0c;轻松下载和合并m3u8视频文件。没错&#xff0c;就是那种分段的流媒体视频。准备好了吗&#xff1f;让我们开始吧&#xff01; 准备工作 在动手之前&#xf…

HarmonyOS4+NEXT星河版入门与项目实战(22)------动画(属性动画与显示动画)

文章目录 1、属性动画图解2、案例实现-小鱼移动游戏1、代码实现2、代码解释3、资源图片4、实现效果3、显示动画4、案例修改-显示动画5、总结1、属性动画图解 这里我们用一张完整的图来汇整属性动画的用法格式和使用的主要属性范围,如下所示: 2、案例实现-小鱼移动游戏 1、代…

【rustdesk】客户端和服务端的安装和部署(自建服务器,docker,远程控制开源软件rustdesk)

【rustdesk】客户端和服务端的安装和部署&#xff08;自建服务器&#xff0c;docker&#xff09; 一、官方部署教程 https://rustdesk.com/docs/zh-cn/client/mac/ 官方服务端下载地址 https://github.com/rustdesk/rustdesk-server/releases 我用的docker感觉非常方便&am…

Qt程序发布及打包成exe安装包

参考:Qt之程序发布以及打包成exe安装包 目录 一、简述 Qt 项目开发完成之后,需要打包发布程序,而因为用户电脑上没有 Qt 配置环境,所以需要将 release 生成的 exe 文件和所依赖的 dll 文件复制到一个文件夹中,然后再用 Inno Setup 打包工具打包成一个 exe 安装包,就可以…

python学opencv|读取图像

【1】引言 前序学习了使用matplotlib模块进行画图&#xff0c;今天开始我们逐步尝试探索使用opencv来处理图片。 【2】学习资源 官网的学习链接如下&#xff1a; OpenCV: Getting Started with Images 不过读起来是英文版&#xff0c;可能略有难度&#xff0c;所以另推荐一…

数据结构 ——— 归并排序算法的实现

目录 归并排序的思想 归并排序算法的实现 归并排序的思想 将已经有序的子序列合并&#xff0c;得到完全有序的序列&#xff0c;即先使每个子序列有序后&#xff0c;再使子序列段间有序 若将两个有序表合并成一个有序表&#xff0c;称为二路归并 归并排序步骤示意图&#x…

Springboot项目搭建(6)-前端登录跳转与Pinia实用

1.添加响应错误拦截 文件地址&#xff1a;src\utils\request.js import axios from axios import { ElMessage } from element-plus const baseURL /api const instance axios.create({baseURL}) //添加拦截器 instance.interceptors.response.use(result>{&#x1f447…

多输入多输出 | Matlab实现TCN-LSTM时间卷积神经网络结合长短期记忆神经网络多输入多输出预测

多输入多输出 | Matlab实现TCN-LSTM时间卷积神经网络结合长短期记忆神经网络多输入多输出预测 目录 多输入多输出 | Matlab实现TCN-LSTM时间卷积神经网络结合长短期记忆神经网络多输入多输出预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 多输入多输出 | Matlab实现…

C++网络编程:select IO多路复用及TCP服务器开发

C网络编程&#xff1a;使用select实现IO多路复用 一、什么是 IO 多路复用&#xff1f;二、IO多路复用器 select三、相关接口3.1、fd_set 结构体3.2、宏和函数 四、select 实现 TCP 服务器五、总结 一、什么是 IO 多路复用&#xff1f; 在网络编程中&#xff0c;最容易想到的并…

HDU Go Running(最小点覆盖 + 网络流优化)

题目大意&#xff1a;有一条无限长跑道&#xff0c;每个人可以规定自己跑步的方向&#xff0c;起点&#xff0c;跑步起止时间。每个人跑步的速度都是1m/s。最后从监控人员哪里得到了n个报告&#xff0c;每个报告给出了某人在某一时候所在的位置&#xff0c;问跑步的最少可能人数…

28.UE5实现对话系统

目录 1.对话结构的设计&#xff08;重点&#xff09; 2.NPC对话接口的实现 2.1创建类型为pawn的蓝图 2.2创建对话接口 3.对话组件的创建 4.对话的UI设计 4.1UI_对话内容 4.2UI_对话选项 4.3UI_对话选项框 5.对话组件的逻辑实现 通过组件蓝图&#xff0c;也就是下图中的…

Reachy 2,专为AI与机器人实验室打造的卓越开源双臂移动操作平台!

近期&#xff0c;花粉机器人&#xff08;POLLEN ROBOTICS&#xff09;隆重推出Reachy 2仿生机器人——下一代开源操作平台&#xff0c;为AI与机器人实验室带来理想的双臂移动操作科研平台&#xff01; Reachy 2的仿生性&#xff1a; 》拥有两个基于Maxon无刷电机的仿生7自由度…

python的openpyxl库设置表格样式:字体/边框/对齐/颜色等

学习目录 1. 安装和使用openpyxl库设置表格样式 2 设置字体font 3 设置边框 4 设置对齐方式 5 设置单元格数据格式 6 设置行高和列宽 7 填充单元格颜色 附录-关于颜色说明 本章节主要介绍如何使用openpyxl库设置表格中的一些样式&#xff0c;比如字体&#xff0c;边框…

Git旧文件覆盖引发思考

一天&#xff0c;我的同事过来找到我&#xff0c;和我讲&#xff1a;张叫兽&#xff0c;大事不好&#xff0c;我的文件被人覆盖了。git是真的不好用啊 git不好用&#xff1f;文件被覆盖&#xff1b;瞬间我似乎知道了什么&#xff0c;让我想到了某位男明星的语法&#xff1a;他…

QSqlTableModel的使用

实例功能 这边使用一个实例显示数据库 demodb 中 employee 数据表的内容&#xff0c;实现编辑、插入、删除的操作&#xff0c;实现数据的排序和记录过滤&#xff0c;还实现 BLOB 类型字段 Photo 中存储照片的显示、导入等操作&#xff0c;运行界面如下图&#xff1a; 在上图中…

什么是代理,nodenginx前端代理详解

一. 什么是代理&#xff1f; 代理就是通过一个特殊的网络服务去访问另一网络服务的一种间接访问方式。像我们不能直接访问国外的网站&#xff0c;只能使用VPN&#xff0c;就是使用了代理 二. 前端为什么要用代理&#xff1f; 首先明确以下两个概念 &#xff08;1&#xff09…

java脚手架系列16-AI大模型集成

之所以想写这一系列&#xff0c;是因为之前工作过程中有几次项目是从零开始搭建的&#xff0c;而且项目涉及的内容还不少。在这过程中&#xff0c;遇到了很多棘手的非业务问题&#xff0c;在不断实践过程中慢慢积累出一些基本的实践经验&#xff0c;认为这些与业务无关的基本的…

网络安全中的数据科学如何重新定义安全实践?

组织每天处理大量数据&#xff0c;这些数据由各个团队和部门管理。这使得全面了解潜在威胁变得非常困难&#xff0c;常常导致疏忽。以前&#xff0c;公司依靠 FUD 方法&#xff08;恐惧、不确定性和怀疑&#xff09;来识别潜在攻击。然而&#xff0c;将数据科学集成到网络安全中…

【算法day1】数组:双指针算法

题目引用 这里以 1、LeetCode704.二分查找 2、LeetCode27.移除元素 3、LeetCode977.有序数组的平方 这三道题举例来说明数组中双指针的妙用。 1、二分查找 给定一个 n 个元素有序的&#xff08;升序&#xff09;整型数组 nums 和一个目标值 target &#xff0c;写一个函数搜…

open-instruct框架使用记录:只使用huggingface数据集的小部分进行训练,如何修改dataset_info.json文件

open-instruct框架 这篇笔记主要记录以下问题&#xff1a;只使用huggingface下载的数据集中的一小部分数据进行数据训练。而且我不想修改open-instruct的加载数据集的代码&#xff0c;以及脚本中的--dataset_mixer_list参数的指定等。下面是我的思路历程。 if args.dataset_na…