(四)提升系统的“读”性能
缓存
本地缓存
比如
Caffeine
缓存不太容易变化的数据(保障缓存一致性)
缓存数据量较小的数据(不会受到内存的限制)
如元数据、配置数据:启动的时候,从DB加载到内存的缓存块里。这样每次读这些数据的时候,就不用访问DB了
中心化缓存
比如
Redis
- 针对用户请求相关数据的缓存
- 用户流量触发的DB的数据(查询)
- 业务计算的数据
- 返回结果的数据
缓存更新策略
cache aside pattern
(经典缓存更新策略)采用的是写数据时删除缓存中旧值,读数据时更新新值
但如果有一个读请求和一个写请求几乎同时到达DB
读请求先访问数据库,将(旧)值写入缓存时有些延迟
而写请求后访问数据库(新值),但把缓存删完了,读请求才开始写入缓存
这就造成缓存数据是旧的
解决方案
保证即使把旧值更新到cache的情况,在后面的某一个时间点也能淘汰掉有问题的缓存
sequenceDiagram Server->>Database: 1.写入数据 Server->>Cache: 2.删除缓存 Server->>Database: 3.读取数据 Server->>Cache: 4.写入缓存(带过期时间)
缓存雪崩
方案一
随机过期时间
方案二
缓存分片:建立缓存集群,根据key的不同情况访问不同的缓存分片。这样即使有一个分片挂掉,那最多也就是一部分数据挂掉了
缓存当DB用
在流量大 + 产品多 + 变化多的典型类电商场景下
- DB扛不住,原因显而易见
- 缓存也扛不住:频繁失效,起不到作用
在限制条件全部满足的情况下
- 只读场景(注意,是只!读!场!景!),因为仅写到cache有数据丢失的风险
- 有高可用方案(如
Redis
、memcached
这种,保证cache不挂)
可以采用:
- DB定时同步数据到cache
- server把cache当DB用
DB读写分离
经典方案
有主DB和备DB
往主DB写,通过
binlog
等方式和备DB数据同步,然后在备DB读
好处:理论上DB读服务可以无限扩容、读库可以单独建索引
问题:主备库数据不一致风险
解决数据不一致
方案一
最常用
直接不管:有些业务不要求即时响应,而且主备同步大概也就百毫秒级别
方案二:结合产品设计
简单
让前端整点加载动画,本质上就是延迟用户的读行为
方案三:强制读主
也行
强一致性读主库,弱一致性读备库
方案四:缓存路由
复杂度较高,不推荐
写DB同时写个百毫秒(超过同步时间)超时的缓存
- 没读到:备库更完了,读备库
- 读到了:那就用呗,保新
并发思维
有一个文件,有1亿条交易数据,从中找出交易金额最大的前100条数据
- 你是不是开始考虑如何快速读取文件了?
- 你是不是开始考虑用什么排序算法了?
- 你是不是想到内存可能不够用了?
你首先应该想到,如何使用更多的资源来并发处理:
拆分数据,投递各机器,分片排序前100条,再对汇总数据排序
异步化设计
对于qps高的功能,只做最重要的事
不影响主业务的步骤异步执行,先返回主业务结果(使用观察者模式)
优化产品方案
- 分页查询,减少一次查询量
- 递进展示
- 降低极致的准确性要求
- 高峰流量期间降级部分功能
- 控制(主动或者被动)重试
其他一些读性能优化手段
- 优化协议
- 流量拦截
- 静态缓存
- 数据压缩
- …
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 OrionLi's Blog!