第 5 周 - 总结
一 分布式缓存构架
在正式讨论缓存之前,很有必要区分一下缓存和缓冲的概念:
缓存(Cache)是介于数据访问者与数据源之间的一种高速存储,当热点数据需要多次读取的时候,用于加快读取的速度。
缓冲(Buffer)是不同速率设备数据互相传输用来临时缓冲存放数据的地方。
其实缓存无处不在
买电脑的时候你可能会关注CPU一级缓存、二级缓存有多大,磁盘整理的时候你可能还会发现操作系统缓存的数据不让移动,另外还有数据库缓存、JVM编译缓存、存储在电信机房的CDN缓存、代理与反向代理缓存、前端静态数据缓存、应用程序缓存、分布式对象缓存等等。
总之就是一些静态数据,或数据变动不敏感,又经常被使用,而每次从数据源获取的代价太高了,于是就找个地方存越来,方便下次快速拿来使用,当然也可以设置数据过期的时间,周期刷新数据。
缓存数据存储Hash表
根据KEY取数据,通过计算Key的HashCode快速定位到存储位置,由于HashCode有可能重复,所以如果Key对应的对象不是唯一的,需要再校验key的一致性。通常HashCode就能过滤筛选掉大部份数据,所以校验的压力并不大,就也是为什么我们要为实体(Entity)写 hashCode()、equals()方法的原因。
缓存的关键指标
缓存命中率
如果缓存一直没被使用,是没有意义的,根据命中率的统计,低命中率数据就会下沉到队列尾部,在资源不足时被释放。
影响命中率的指标
缓存键集合大小
键集合越大,数据越散列,命中率低,比如根据订单ID缓存订单,而操作同一个商品的次数并不多见,缓存某个用户的订单并不具有高复用性,缓存没有意义,应该缓存高命中的热点数据。
缓存可以使用内存空间
缓存空间小,缓存的数据少,命中率低
缓存对象生存时间
生存时间越短,命中率越低
代理缓存
设置在客户端,代理多个用户对外请求数据,为减少重复对外请求同一资源,而对资源进行缓存,以供机构内部人员复用。
反向代理
代理服务端,缓存外部请求资源,以减轻服务端压力,将请求响应的数据写入缓存,对相同的请求直接从缓存中拿取数据响应客户端,而不重新请求服务。
多层反向代理
内容分发网络CDN
看似高大尚的CDN,实则没有任何的技术壁垒,谁都能自建。
通读缓存
客户端统一连接通读缓存,通读缓存根据自身情况判断是否需要从新更新数据
旁路缓存
客户端先查找旁路缓存有没有命中数据,没有客户端到服务中拉取数据,再更新到旁路缓存中。
浏览器对象缓存
http协议定义数据过期时间
localStorage.setItem('key',value)
本地对象缓存
HashMap
包括部署在同一台服务器的独立缓存服务
早期的应用场景,现在已倾向服务无(弱)状态
远程分布式对象缓存
缓存节点互不感知,由客户端根据算法路由
Memcached
客户端API
通信模块
路由算法
节点扩容,一致性HASH
缓存节点相互感知
各种介质数据的访问延时
像元素周期表那样背下来吧

技术栈各个层次的缓存
这个要记在账本上吧!总之缓存越靠近客户端越省事。

缓存为什么能显著提升性能
缓存通常存储在高速内存中,在数据生命周期内,拿来就能用,节省了IO、计算的等待时间,因此能够显著地提升性能,到这里是不是该来一波点赞了:
缓存是系统性能优化的大杀器
技术简单
性能提升显著
应用场景多
显然缓存适用很多场景,但以下情况应该慎用缓存
频繁修改的数据:一般来说数据的读写比例在2:1以上缓存才有意义。
没有热点的数据:造成资源的浪费
控制缓存失效的LRU算法,清除没有热点的数据

数据不一致与脏读
应用要能够容忍一段时间的数据不一致,数据更新时通知缓存失效,删除缓存数据是更加稳妥的做法。不存在的数据也缓存越来,防止缓存穿透。
Redis
客户端连接任意节点即可
节点互相感知
二 消息队列与异步构架
同步与异步调用
如果被调用服务不能及时响应,意味着同步调用的调用方需要阻塞线程等待被调用方执行。异步调用则对该场景进行了优化,被调方接收到请求时先把请求存储压入队列,并立即返回告之调用方请求已接收,等被调方消息处理线程处理消息后,回调调用方。
消息队列
角色
消息生产者
消息队列
消息消费者
模型
点对点模型

发布订阅模型
一个生产者,多个消费者
消息队列的好处
异步处理,提升(写)性能操作
更好的伸缩性,可单独对某消息处理进行伸缩
削峰填谷
不会因为请求并发影响到处理并发,可按照自己的并发能力进行消息处理
失败隔离和自我修复
发布者不直接依赖消费者,可以 消费者处理错误与生产者隔离。
生产者与消费者互相不受对方失败影响。
解耦,互不感知,不知道对方
两者约定消息格式即可
事件驱动架构EDA
主要MQ产品比较
RabbitMQ
ActiveMQ
RocketMQ
Kafka
可根据搜索引擎返回结果,了解产品的活跃度
负载均衡构架
重定向负载均衡
性能差,将服务IP直接暴露给客户端
DNS负载均衡
不同用户返回不同IDC机房IP地址,不用每次都解析DNS
反向代理负载均衡
基于HTTP通信,吞吐率低,通常用在小型系统中。
IP负载均衡
在IP层做负载均衡,处理的是tcp包
缺点:请求和响应都在经过负载均衡
链路层负载均衡
响应包不经过负载均衡,直接发送给客户端
负载均衡算法
轮询
加权轮询
随机
最少连接
源地址散列(没啥用)
应用服务器Session管理
Session 复制:集群规模受限,淘汰
Session绑定:会话绑定(源地址散列)
风险:业务快速迭代,应用发布会话丢失
Cookie记录Session
每次请求带上Cookie状态
网络开销大
装饰模式的应用,从request中获取Session
Session 服务器
分布式数据库
MySQL主从复制
更新,新增、删除操作写入BinLog,复用BinLog进行同步
通常关闭DDL同步,DDL新增、删除字段一定要在新版应用程序发布之前,避免在线修改数据库被阻塞。
读写分离
主主复制
不能同时向两个服务器写数据,导致数据冲突
更新DDL会导致极大的同步延迟
极端场景:无法解决主服务器故障导致最后数据无法同步到从服务器而从服务马上要切换为主服务器的场景。
BinLog不会重复传播
评论