缓存

本地缓存

比如Caffeine

  • 缓存不太容易变化的数据(保障缓存一致性)

  • 缓存数据量较小的数据(不会受到内存的限制)

    元数据配置数据:启动的时候,从DB加载到内存的缓存块里。这样每次读这些数据的时候,就不用访问DB了

中心化缓存

比如Redis

  • 针对用户请求相关数据的缓存
    • 用户流量触发的DB的数据(查询)
    • 业务计算的数据
    • 返回结果的数据

缓存更新策略

cache aside pattern(经典缓存更新策略)采用的是写数据时删除缓存中旧值,读数据时更新新值

但如果有一个读请求和一个写请求几乎同时到达DB

读请求先访问数据库,将(旧)值写入缓存时有些延迟

而写请求后访问数据库(新值),但把缓存删完了,读请求才开始写入缓存

这就造成缓存数据是旧的

解决方案

保证即使把旧值更新到cache的情况,在后面的某一个时间点也能淘汰掉有问题的缓存

缓存雪崩

方案一

随机过期时间

方案二

缓存分片:建立缓存集群,根据key的不同情况访问不同的缓存分片。这样即使有一个分片挂掉,那最多也就是一部分数据挂掉了

缓存当DB用

在流量大 + 产品多 + 变化多的典型类电商场景下

  • DB扛不住,原因显而易见
  • 缓存也扛不住:频繁失效,起不到作用

在限制条件全部满足的情况下

  • 只读场景(注意,是只!读!场!景!),因为写到cache有数据丢失的风险
  • 有高可用方案(如Redismemcached这种,保证cache不挂)

可以采用:

  • DB定时同步数据到cache
  • server把cache当DB用

DB读写分离

经典方案

  • 有主DB和备DB

  • 往主DB写,通过binlog等方式和备DB数据同步,然后在备DB读

好处:理论上DB读服务可以无限扩容、读库可以单独建索引

问题:主备库数据不一致风险

解决数据不一致

方案一

最常用

直接不管:有些业务不要求即时响应,而且主备同步大概也就百毫秒级别

方案二:结合产品设计

简单

让前端整点加载动画,本质上就是延迟用户的读行为

方案三:强制读主

也行

强一致性读主库,弱一致性读备库

方案四:缓存路由

复杂度较高,不推荐

写DB同时写个百毫秒(超过同步时间)超时的缓存

  • 没读到:备库更完了,读备库
  • 读到了:那就用呗,保新

并发思维

有一个文件,有1亿条交易数据,从中找出交易金额最大的前100条数据

  • 你是不是开始考虑如何快速读取文件了?
  • 你是不是开始考虑用什么排序算法了?
  • 你是不是想到内存可能不够用了?

你首先应该想到,如何使用更多的资源并发处理:

拆分数据,投递各机器,分片排序前100条,再对汇总数据排序

异步化设计

对于qps高的功能,只做最重要的事

不影响主业务的步骤异步执行,先返回主业务结果(使用观察者模式)

优化产品方案

  • 分页查询,减少一次查询量
  • 递进展示
  • 降低极致的准确性要求
  • 高峰流量期间降级部分功能
  • 控制(主动或者被动)重试

其他一些读性能优化手段

  • 优化协议
  • 流量拦截
  • 静态缓存
  • 数据压缩