软件架构与思考

👉 所有文章
高性能 高性能设计模式
数据库 数据库分库分表指南 MySQL 建表参考 MySQL 初始化数据的一些方案 数据版本号
分布式 ID 分布式 ID 生成方案探讨
缓存 缓存 使用数据版本号保证缓存是最新数据 基于redis的二级缓存
微服务 如何实现远程调用 RPC 协议中的数据签名验签和加解密方案探讨 关于服务间调用循环依赖的一些思考 我所理解的负载均衡 一致性哈希 基于Redis的分布式会话管理系统 如何部署服务 灰度发布 如何区分上游和下游 日志级别
算法与协议 一个可扩展的 MQ 消息设计 Dynamo涉及的算法和协议 写时复制
任务分发 Gearman入门 如何使用redis构建异步任务处理程序
安全 关于对账的一些理解 一个简单可靠的 Dubbo 请求/响应数据签名方案
其他 使用卫语句减少 if else 嵌套

缓存


目录:


有哪些缓存?

  • CPU 多级缓存
  • 浏览器缓存
  • 数据库缓存
  • CDN
  • 业务数据缓存
  • 等等

按照进程内外科分为:

  • 进程内缓存(如 guava 库提供的 Java 缓存类)
  • 进程外缓存(如 redis 缓存,是位于业务代码运行进程实例之外的)

缓存的目的

加速。

业务缓存的选型

  • redis
  • memcache
  • ehcache
  • guava
  • ...

容量评估

  1. 业务高峰期,内存使用占比会达到多少?
  2. 当缓存可用区间为0时,会发生什么?
  3. 缓存占用空间达到一定阈值时,如何告警?

静态缓存和动态缓存

静态缓存,是指内容不会变的缓存。例如一段文字的MD5是不会变的。

动态缓存,缓存的是动态数据,需要经常更新的。例如某个网站的文章数量。

强一致性与最终一致性

数据会持久化到 DB 中(如 MySQL),引入缓存后,DB 和缓存是两套系统,无法做到【单机事务】的效果。

两个选择:

  • 强一致性:引入 2PC、paxos 等方案保证一致性。缺点是复杂性提高,性能变差。
  • 最终一致性:允许缓存中数据和 DB 中数据一段时间内的不一致。

如何更新缓存:以用 redis 存储业务缓存为例

对于动态缓存,一定要考虑下面几件事:

  • 过期时间是多长比较合适?
  • 如果有并发,会不会导致缓存内有旧数据,而无新数据?
  • 引入缓存,是否会侵入现有的业务代码?对主流程的性能等影响能接受吗?

通过 Trade-Off(权衡) ,我们选择合适自己的方案即可。

常见的套路:

套路1

  • 查询:查无缓存,则去DB查,并更新缓存,设置过期时间
  • 更新DB:更新DB 后,清理对应的缓存(为了减少并发导致的缓存脏数据,可以在几秒后再清理一次对应缓存)

套路2

  • 查询:查无缓存,则去DB查,并更新缓存,设置过期时间
  • 更新DB:监听binlog,异步清理对应缓存

套路3

DB 数据增加版本号字段,缓存中也缓存版本号,更新缓存时使用乐观锁/cas。

下面的文章值得参考:

缓存穿透

加上缓存后,某些请求因为在DB中没有数据,所以在缓存中找不到数据,这就导致请求一致会走到DB层。这就是缓存穿透。

例如对用户信息进行缓存,key 是用户id,value 是用户具体信息。如果请求某一个不存在的用户ID,会一致走到DB。

影响

可能导致DB压力未有效缓解。

解决方案1: 尽早拦截非法请求

  • 接入风控,尽早拦截非法请求。

解决方案2: 缓存空值

用户ID 虽然不存在,依然缓存,缓存值是空。

解决方案3: DB请求串行化

利用互斥锁(例如 redis的锁),串行化去DB中查询某一用户ID的信息。注意,这里的锁粒度是用户ID。

没拿到锁的数据怎么办?

  1. 直接报错系统繁忙
  2. 间隔一段时间,获取缓存内容,无则重试加,重试 n 次后无缓存或者拿不到锁,则报错。

缓存击穿

热点 key 突然过期失效,导致大量请求走到 DB 层.

影响

DB压力陡增陡降.

解决方案1: DB请求串行化

见上面的讨论。

缓存雪崩

同一时间大量缓存时效,导致大量请求走到DB,DB 性能急剧下降。

解决方案1:限流

对相关接口限流。

解决方案2:失效时间随机化

失效时间不要做成固定值,而是用一定范围内的随机值。

(待完善)


( 本文完 )

文章目录