1. 前言你好哇!我是小翔 。之前写了三篇 #Golang 并发编程 的文章了,这次来换换口味,开个 手撕源码 的新坑!一起来扒一扒 Go 语言高性能 local cache 库 bigcache , 看看能不能把开源大佬们的骚操作带到项目里去装一装(?)
2. 为什么要学习开源项目个人认为学习开源项目的收益:
- 跟进社区,不做井底之蛙 看到一个开源项目,可以思考下:大佬们最近都在解决哪些问题?他们用到了哪些开源工具?我能拿到项目里用吗?这玩意有 bug 吗?要不要提个 issue 或者提个 PR 呢?
- 面向原理编程 我们在实际项目中会用上很多开源库/框架,你是否好奇过它们的实现机制呢?理解用到的库的实现机制,能帮我们避开很多坑,堪称降维打击
- 学习优秀的设计 优秀的开源项目经过了成千上万开发者的 review,质量一般会比公司赶进度赶出来的质量高得多得多,从中学习优秀的设计,再在实际项目中多用用,同事会感叹:
3.2 bigcache 诞生背景bigcache 的开发者是 allegro,是波兰的一个电商网站,参考资料中给出了他们的技术博客的原文 , 文中详细描述了他们问题的背景以及思考,值得研究 。他们的需求主要是:
- 用 HTTP 协议处理 GET POST 请求,body 不大
- 10k rps(requests per second) 5k 读 5k 写
- 缓存至少 10 分钟
- 低延时:平均 5ms ,P99 < 10ms,P999 < 400ms总结一下,他们需要一个快速、支持过期淘汰、支持 RESTful api 的字典服务
- 百万级缓存项时响应速度也很快
- 并发安全
- 支持设置过期时间
如 bigcache.go 的声明,我们初始化得到的 BigCache,核心实际上是一个 []*cacheShard , 缓存的写入、淘汰等核心逻辑都在 cacheShard 中了
type BigCache struct {shards[]*cacheShardlifeWindow uint64clockclockhashHasherconfigConfigshardMaskuint64closechan struct{}}
那么在写入一个 key value 缓存时,是如何做分片的呢?func (c *BigCache) Set(key string, entry []byte) error {hashedKey := c.hash.Sum64(key)shard := c.getShard(hashedKey)return shard.set(key, hashedKey, entry)}
这里会首先进行一次 hash 操作 , 将 string key hash 到一个 uint64 类型的 key 。再根据这个数字 key 去做 shardingfunc (c *BigCache) getShard(hashedKey uint64) (shard *cacheShard) {return c.shards[hashedKey&c.shardMask]}
这里把取余的操作用位运算来实现了,这也解释了为什么在使用 bigcache 的时候需要使用 2 的幂来初始化 shard num 了cache := &BigCache{shards:make([]*cacheShard, config.Shards),lifeWindow: uint64(config.LifeWindow.Seconds()),clock:clock,hash:config.Hasher,config:config,// config.Shards 必须是 2 的幂// 减一后得到一个二进制结果全为 1 的 maskshardMask:uint64(config.Shards - 1),close:make(chan struct{}),}
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 离婚7年!王子豪亲手撕掉了自己母亲马蓉的“遮羞布”
- 手撕鬼子就算了,输血管也能忍了,但古装配黑丝,是真的离谱!
- 如何使用Netty模拟一个Web服务端
- 玩手机就算了,手撕鬼子也忍了,但古装配黑丝,真的别太离谱啊!
- 手撕鬼子就算了,玩手机也忍了,但古装配黑丝,是真的离谱啊!
- Golang 中的 IO 包详解:常用的可导出函数详解
- 清华发布SmartMoE:一键实现高性能MoE稀疏大模型分布式训练
- Golang中降本增效的常规实践
- “京圈太子妃”白百何:一夜狂赚8000万,手撕杨颖炮轰王珞丹等人
- 正宗手撕鸡是蒸的还是煮的 正宗手撕鸡是蒸的还是煮的好吃