golang缓存框架学习

golang主流缓存组件

本地缓存场景中,一般使用map或者缓存框架,为了线程安全,会使用sync.Map或线程安全的缓存框架。缓存组件主要需要解决的几个问题:

  1. 需要较高的读写性能+命中率
  2. 支持按写入时间过期
  3. 支持淘汰策略
  4. 解决GC问题,大量对象写入会引起STW扫描标记时间过长,CPU毛刺严重

需要考虑数据类型对GC的影响,如果缓存key和value都是基本类型(不包含指针,string类型底层也是指针+Len+Cap),gc便不会进行扫描,因此在进行缓存框架的使用时,需要根据业务数据量的数量级,考虑数据类型对GC的影响,选定合适的缓存框架。常见的缓存框架分为两类:

  • 零GC: 例如freecache,bigcache,底层数据基于ringbuf,减小指针个数
  • 有GC:直接基于Map实现的缓存框架

常见开源本地缓存组件汇总:
image

解决GC问题的方案:

  • 无GC:分配堆外内存(Mmap)
  • 避免GC:map非指针优化(map[uint64]unit32)或者采用slice实现一套无指针的map
  • 避免GC:数据存入[]byte slice(循环队列)

解决性能问题的方案:

  • 数据分片(降低锁粒度)

freecache

项目地址

实现

freecache中通过segment对数据进行分片,freecache内部包含256个segment,每个segment维护一把互斥锁,每条key/value数据进来后会先根据key计算hash值,然后根据hash值决定当前的这条数据落入到哪个segment中。

对于每个segment,由索引和数据两部分构成。

索引: 索引常用的最简单的方式是采用map[uint64]unit32来维护,但freecache并没有采用这种方式,而是通过slice来底层实现一套无指针的map,来避免GC扫描。

数据: 数据采用缓冲区ringBuf来循环使用,底层采用[]byte进行封装实现。数据进入ringBuf后,记录写入的位置index作为索引,读取时首先读取数据header信息,再读取key/value数据。

数据传递过程: freecahe -> segment -> slot,ringbuffer

框架结构

image

总结

freecache利用数据分片减小锁的粒度,在存储数据时采用自建的map减少指针避免GC,数据存储时采用预先分配内存然后循环使用。

bigcache

项目地址

实现

与freecache类似,数据进行分片,每个bigcache包含2^n个cacheShard(默认1024),每个cacheShard对象维护一把sync.RWLock锁,数据分散到不同的cacheShard中。

每个cacheShard由索引和数据两部分构成。

索引: 索引采用map[uint64]uint32存储,索引中存储的是该条数据在entryBuffer写入的位置pos。

数据: 数据采用entry([]byte)环形队列存储,每条key/value数据按照TLV格式写入队列。

框架结构

image

总结

bigcache实现方法大致与freecache相同,不同点在于索引存储时更为巧妙,采用go内置的map结构加上基础数据类型来实现。同时底层存储数据的队列也可以根据空间大小来决定是否扩容。唯一缺陷是无法针对每个key进行设置不同的过期时间。
bigcache性能优化

groupcache

项目地址

posted @ 2024-03-08 14:49  BrandonV  阅读(96)  评论(0)    收藏  举报