1.鹅厂微创新Golang缓存组件TCache介绍
2.浅谈Golang两种线程安全的码分map
鹅厂微创新Golang缓存组件TCache介绍
一个 Golang 自研小组件,TCache 介绍
作者:frank、码分maxy、码分lark 等。码分
TCache 是码分一个 Golang 团队自研的缓存组件,旨在优化视频会员场景下高并发请求的码分张润涛 源码压力,减少底层存储压力,码分提升系统可用性。码分设计时,码分我们考虑了开源组件如布隆过滤器、码分位图、码分localcache 的码分特点和优劣,以业务需求为出发点,码分集成这些组件形成整体解决方案。码分
TCache 设计目标
主要目标是码分为视频会员服务提供高效缓存,应对大量 APP 请求,减轻存储层压力,并增强系统稳定性。经过调研,我们发现现有开源组件适合不同场景,徐州溯源码因此决定整合这些组件,通过配置化设计,让业务根据自身需求选择合适的缓存策略。
整体架构
TCache 分为四层架构:业务场景层、中间件层、组件层与算法层。业务场景层直接与应用交互,中间件层集成了多种缓存算法,组件层基于开源组件实现,算法层则深入研究缓存技术原理。
组件结构
TCache 集成了多种缓存组件,包括 KV 型结构 Cache、BitMap、BloomFilter 与大型计数器 Hyperloglog。此外,我们计划集成更多组件以覆盖更多业务场景。
Cache 组件设计
提供了统一的 cache 接口,支持用户自定义底层缓存实现,包括默认实现与本地缓存组件 localcache 的文雅源码公式接口定义。
BitMap 组件设计
BitMap 组件集成经典 BitMap 与 Roaring 位图算法,提供单一操作 API,便于业务集成使用。组件结构清晰,代码接口明确。
开发过程
TCache 的开发过程始于团队转型 Golang 时的技术积累与开源组件分析,通过源码阅读、论文研读,深入了解组件技术,最终形成组件化设计。团队持续研究缓存替换算法、位图算法,通过实验对比分析,提炼出业务适用的缓存策略。
功能分析
本地缓存强调数据一致性与吞吐量,支持多线程访问与内存限制,适用于缓存热点数据。常见组件如 freecache、fastcache、pda app源码bigcache 等,提供线程安全、高命中率与高效管理的特性。
源码分析
深入研究开源组件,如 BigCache、BloomFilter、RoaringBitmap,通过建模与代码分析,了解组件实现原理与优化策略。
算法研究
研究缓存替换算法,包括 Belady 最优策略、随机策略、先进先出、最近不使用、最不经常使用、重引用间隔预测等。通过实验对比分析,提炼出适用于不同场景的缓存策略。
实验研究
通过功能与性能对比研究,lutter项目源码推荐不同缓存组件在特定场景下的应用,如 freecache、bigcache、fastcache、localcache 等,以及针对数据持久化与热启动的组件。
组件化
整合多种组件形成 TCache,通过组件化设计,让业务灵活选择缓存策略,提高系统性能与稳定性。
总结
TCache 的开发是一个无心插柳的成果,整合了团队的技术积累与业务需求。通过研究、实验与优化,我们找到了适合视频会员服务的缓存解决方案。未来,结合 AIGC 等新技术,开发出更多原创组件,有可能推动开发行业的变革。
浅谈Golang两种线程安全的map
文章标题:浅谈Golang两种线程安全的map
导语:本文将深入探讨Golang中的本地缓存库选择与对比,帮助您解决困惑。
Golang map并发读写测试:
在Golang中,原生的map在并发场景下的读写操作是线程不安全的,无论key是否相同。具体来说,当并发读写map的不同key时,运行结果会出现并发错误,因为map在读取时会检查hashWriting标志。如果存在该标志,即表示正在写入,此时会报错。在写入时,会设置该标志:h.flags |= hashWriting。设置完成后,系统会取消该标记。
使用-race编译选项可以检测并发问题,这是通过Golang的源码分析、文章解析和官方博客中详细解释的。
map+读写锁实现:
在官方sync.map库推出之前,推荐使用map与读写锁(RWLock)的组合。通过定义一个匿名结构体变量,包含map、RWLock,可以实现读写操作。
具体操作方法如下:从counter中读取数据,往counter中写入数据。然而,sync.map和这种实现方式有何不同?它在性能优化方面做了哪些改进?
sync.map实现:
sync.map使用读写分离策略,通过空间换取时间,优化了并发性能。相较于map+RWLock的实现,它在某些特定场景中减少锁竞争的可能性,因为可以无锁访问read map,并优先操作read map。如果仅操作read map即可满足需求(如增删改查和遍历),则无需操作write map,后者在读写时需要加锁。
sync.map的源码深入分析:
接下来,我们将着重探讨sync.Map的源码,以理解其运作原理,包括结构体Map、readOnly、entry等。
sync.Map方法介绍:
sync.Map提供了四个关键方法:Store、Load、Delete、Range。具体功能如下:
Load方法:解释Map.dirty如何提升为Map.read的机制。
Store方法:介绍tryStore函数、unexpungeLocked函数和dirtyLocked函数的实现。
Delete方法:简单总结。
Range方法:简单总结。
sync.Map总结:
sync.Map更适用于读取频率远高于更新频率的场景(appendOnly模式,尤其是key存一次,多次读取且不删除的情况),因为在key存在的情况下,读写删操作可以无锁直接访问readOnly。不建议用于频繁插入与读取新值的场景,因为这会导致dirty频繁操作,需要频繁加锁和更新read。此时,github开源库orcaman/concurrent-map可能更为合适。
设计点:expunged:
expunged是entry.p值的三种状态之一。当使用Store方法插入新key时,会加锁访问dirty,并将readOnly中未被标记为删除的所有entry指针复制到dirty。此时,之前被Delete方法标记为软删除的entry(entry.p被置为nil)都会变为expunged状态。
sync.map其他问题:
sync.map为何不实现len方法?这可能涉及成本与收益的权衡。
orcaman/concurrent-map的适用场景与实现:
orcaman/concurrent-map适用于反复插入与读取新值的场景。其实现思路是对Golang原生map进行分片加锁,降低锁粒度,从而达到最少的锁等待时间(锁冲突)。
它实现简单,部分源码如下,包括数据结构和函数介绍。
后续:
在其他业务场景中,可能需要本地kv缓存组件库,支持键过期时间设置、淘汰策略、存储优化、GC优化等功能。此时,可能需要了解freecache、gocache、fastcache、bigcache、groupcache等组件库。
参考链接:
链接1:/questions//golang-fatal-error-concurrent-map-read-and-map-write/
链接2:/golang/go/issues/
链接3:/golang/go/blob/master/src/sync/map.go
链接4:/orcaman/concurrent-map