惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

P
Privacy International News Feed
H
Help Net Security
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Blog — PlanetScale
Blog — PlanetScale
T
The Blog of Author Tim Ferriss
Y
Y Combinator Blog
Microsoft Security Blog
Microsoft Security Blog
Stack Overflow Blog
Stack Overflow Blog
Jina AI
Jina AI
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
D
Docker
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
I
InfoQ
Engineering at Meta
Engineering at Meta
H
Heimdal Security Blog
月光博客
月光博客
有赞技术团队
有赞技术团队
S
SegmentFault 最新的问题
V
Vulnerabilities – Threatpost
K
Kaspersky official blog
C
Cisco Blogs
Microsoft Azure Blog
Microsoft Azure Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
MongoDB | Blog
MongoDB | Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
L
LINUX DO - 热门话题
H
Hackread – Cybersecurity News, Data Breaches, AI and More
T
Tor Project blog
M
MIT News - Artificial intelligence
L
LINUX DO - 最新话题
P
Privacy & Cybersecurity Law Blog
Google DeepMind News
Google DeepMind News
宝玉的分享
宝玉的分享
Security Archives - TechRepublic
Security Archives - TechRepublic
IT之家
IT之家
博客园_首页
F
Full Disclosure
T
Threatpost
www.infosecurity-magazine.com
www.infosecurity-magazine.com
阮一峰的网络日志
阮一峰的网络日志
Security Latest
Security Latest
S
Security @ Cisco Blogs
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
AI
AI
Hacker News - Newest:
Hacker News - Newest: "LLM"
H
Hacker News: Front Page
A
Arctic Wolf
C
Check Point Blog
S
Securelist
A
About on SuperTechFans

Posts on WKLKEN THINKING

apisix 中的 lrucache apisix 中的服务发现机制 apisix 中的负载均衡 apisix etcd机制 聊聊框架 关于 k8s 的 zero downtime deployment 一些建议 apisix 遇到的一些问题 关于在除夕前一天换了一个洗衣机的故事 Django DRF 性能优化 DRF 的一些实践 Part1: Serializer DRF继承关系图 Better Code: 关于接口的灵活性 新的仓库: wklken/naming Better Code: 抽象: 可扩展性与可维护性的抉择 Better Code: 异常时, 该提示用户哪些信息? Better Code: 更好的异常日志打印 Go: some libs Go: go-redis/cache升级的坑 Go: logrus性能提升 Go: gin validation 远程办公的一点总结 Go: 开发过程中的一些bug 项目管理实践: 风险驱动开发 Go: 一种error wrap调用链处理方式 漫谈技术选型 Go: 基于 apitest 做handler层单元测试 Go: go-sql-driver interpolateparams参数优化 [分享]深度工作 你需要更多的思考时间 Django项目重构小结 工作七年小结: 学习,生活及其他 [分享]bash日常: bash-utils 极客时间推广海报 2017总结: 予时光以意义 k8s APIServer源码: api注册详细细节 k8s APIServer源码: api注册主体流程 k8s APIServer源码: 服务启动 k8s APIServer源码: go-restful框架 重构 - 读书笔记(Python示例) 写给新人的沟通建议 vim 杂谈 - 关于快速编辑 vim 杂谈 - 关于移动 读书笔记-重构: 章11 处理概括关系 读书笔记-重构: 章10 简化函数调用 读书笔记-重构: 章9 简化表达式 读书笔记-重构: 章8 重新组织数据 读书笔记-重构: 章7 在对象之间搬移特性 读书笔记-重构: 章6 重新组织函数 Python 代码规范小结 [分享]关于vim ElasticSearch集群部署文档 Logstash+ElasticSearch处理mysql慢查询日志 [分享]关于代码调试DE那些事 Logstash+ElasticSearch+Kibana- 实现相对通用的数据收集分析 ELK维护的一些点(二) [分享]Python源码剖析-数据结构 一些Centos Python生产环境的部署命令 摘录<<6个月学会任何一种外语>> ELK 维护的一些点 也许是一个新的开始 一些vim的个性化配置 读书笔记-调试九法 这段时间的一些想法 Python 源码阅读 - 垃圾回收机制 我为什么要写博客 APUE笔记-第一章 UNIX基础知识 Python源码阅读-闭包的实现 Python源码阅读-内存管理机制(二) Python源码阅读-内存管理机制(一) Python-基础-数据结构小结 '活动'设计的一些trick 一些简单的Python测试题 我的tmux配置及说明【k-tmux】 Review and Restart 工作四周年小结 vim插件: surround & repeat[成对符号编辑] vim插件: gundo[时光机] vim插件: expand-region[区域选中] vim插件: quickrun[快速执行] vim插件: trailing-whitespace[行尾空格处理] vim插件: closetag[成对标签补全] vim插件: ctrlp[文件搜索] vim插件: airline[状态栏增强] vim插件: theme[主题] vim插件: tagbar[大纲式导航] vim插件: nerdcommenter[快速注释] vim插件: rainbow_parentheses[括号高亮] vim插件: syntastic[语法检查] vim插件: delimitmate[符号自动补全] vim插件: matchit[成对标签跳转] vim插件: easy-align[快速对齐] vim插件: multiple-cursors[多光标操作] vim插件: vim-signature[快速标记跳转] vim插件: easymotion[快速跳转] vim插件: vundle[管理插件] Elasticsearch几个问题的解决 分享一份 Vim 简介PPT k-vim 更新9.0版本 关于知识管理工具的思考 Logstash+ElasticSearch+Kibana处理nginx访问日志
缓存使用的一些经验
2022-06-26 · via Posts on WKLKEN THINKING

在一个大的项目中, 使用了全缓存模型, 即, 所有数据都会经过cache.

简单分层: 应用->内存缓存->redis缓存->数据库

是一个典型的多读写少的场景, 并且数据量, 请求量非常大.

总结了一些使用经验, 供参考

1. 更新缓存的Design Pattern: 使用Cache aside

简洁优雅

关于缓存更新, 可以阅读这篇文章: CoolShell: 缓存更新的套路

为什么选择Cache Aside Pattern, 因为这个模式足够简单, 出现不一致的概率非常低, 对于大多数项目来说够用了.

而其他几种模式, 复杂度会高很多.

2. 并发很高时, 需要防缓存击穿

当并发很高的时候, 一个热点key失效, 会触发回数据库重查的逻辑, 此时会有大量请求落到数据库

需要做防缓存击穿的处理.

一般各种语言的库, 都有考虑到这一点, 例如 go-redis/cache

如果是golang并且自定义了cache, 可以使用 singleflight, 其他语言也可以找类似机制的库.

这个库很轻量

# define
type Cache struct {
	name              string
	keyPrefix         string
	codec             *cache.Cache
	cli               *redis.Client
	defaultExpiration time.Duration
	G                 singleflight.Group
}

# usage
// if missing, call retrieveFunc
data, err, _ := c.G.Do(key.Key(), func() (interface{}, error) {
		return retrieveFunc(key)
	})

3. 缓存空值, 需要防缓存穿透

如果一个key不存在, 在缓存中查不到, 在数据库中也查不到, 那么这个key的请求每次都会穿透到数据库

此时, 可以引入 bloomfilter 或者 cuckoofilter;

但是, 更简单的做法是, 缓存空值; 当成一个普通的key处理(缓存失效/数据一致性处理等)

4. 总是设置过期时间, 并且带随机数避免缓存雪崩

大部分场景下, 给每一个缓存key设置 TTL是一个很好的习惯. 可以避免无用数据占用资源, 及时淘汰掉使用较少的数据.

但是, 设置TTL的时候, 建议加上一个范围内容的随机数, 避免缓存在同一时间失效, 造成缓存雪崩.

TTL = 900s + randint(0,10)

5. key 中使用namespace+version前缀

key = {namespace}:{version}:{type}:{uniqueKey}

在实际应用部署中, 由于可能跟其他应用共用一套缓存, 所以建议缓存的key加入前缀, 防止冲突(如果冲突, 非常难以debug)

另外, 需要加入一个version, 在版本发布必要时变更, 以弃用缓存中已有的数据

  1. 由于不断迭代开发, 同一个key对应的value可能会变更, 例如value对应的数据结构新增了一个字段, 那么此时缓存中存量的缓存数据是没有这个字段的, 可能会造成一些bug.
  2. 还有另外一个需要特别小心的是, 升级缓存第三方库的时候, 某些版本可能是breaking change, 例如改变了压缩算法, 此时存量数据将无法正确被获取.一个例子: Can’t upgrade from v7 to v8 directly?

6. 缓存结构体, 使用msgpack替代json

MessagePack: It’s like JSON.but fast and small

优点:

缺点:

  • 在redis等服务端debug获取时不是明文, 不是很利于调试

所以, 缓存数据量比较大, 并且对性能有要求的, 可以使用msgpack

7. value比较大, 可以考虑启用压缩

如果value比较大, 那么在放入缓存前, 可以进行一次压缩, 获取后再解压

当然, 这个会产生额外的资源消耗(CPU), 以及会多一些耗时.

但是, 这个有利于减少网络传输中的包大小. 如果读取是非常高频的话, 那么代价还是值得的.

可以参考 go-redis/cache, 当值超过一定大小时使用 s2 compression 进行压缩

8. 批量操作, 使用pipeline

以redis为例, 批量操作

  1. 代码for循环, 一个个获取
  2. 可以考虑使用mget/mhget之类的多个key
  3. 使用pipeline

可以根据key-value特征, 批量key的数量等, 简单压测下性能, 决定使用哪种方式. 正常情况下, key数量较大的时候, pipeline性能最好.

甚至, 代码实现可以根据key的数量, 自行决定使用mget还是pipeline

9. 内存缓存 vs Redis

大部分情况, 项目中会混用两种缓存.

如果对数据一致性要求比较高, 可以全部使用 Redis.

但是, 其实每一次 Redis 操作代价大于内存操作

某些数据, 例如模型, 主键之类的, 一旦确定, 是不会变更的.

此时, 可以考虑使用内存缓存替代.

如果是golang, 推荐使用 go-cache. 没有其他实现那么强大, 但是胜在不需要序列化/反序列化.

10. 多级缓存 and client-side-cache

如果使用的 Redis6, 并且程序的driver支持, 那么可以直接利用 client-side-caching 特性获取最大的性能. 这个对程序透明, 无需在额外的逻辑处理.

但是, 当前(2022)有很多时候, 部署基建还是老版本Redis, 很多语言的driver也还没有支持, 可能复用不了

那么, 此时如果使用了内存->redis两级缓存, 如何确保数据一致性.

可以做的额外操作:

  1. 实现类似redis6 client-side-cache机制, 通过发布订阅等方式实现
  2. 可以使用一个sorted-set存储5分钟内变更的key, 内存缓存TTL设置5分钟; 每次先获取变更key列表, 本地缓存进行时间戳对比(这个方案对于批量key操作性能提升很大, 相当于把 N 次redis操作, 变成 本地缓存+ 1 次 changedkeylist获取+M次redis操作)

11. 配置建议: 同时支持standalone和sentinel配置

让运维根据实际应用场景, 自行切换使用.

成本不高的话, 也可以支持下redis-cluster配置

注意, 开启pool以获取更好的性能

另外, 也需要关注下如何开启prometheus/otel相关的配置, 以便某些情况下, 监测相关的指标

12. 设置开关, 支持withCache/withoutCache调试

引入缓存后, 在进行问题调试的时候非常不变.

建议加入相关的调试标志, 例如?force=true

  • 加上, 全链路数据获取走数据库
  • 没加, 全链路走缓存

此时, 可以通过对比两次请求的差异, 确定是否是缓存问题

甚至, 可以加入?debug=true以获取各个环节的上下文信息, 快速调试