架构师训练营第 1 期第 5 周学习总结
一、缓存
优化系统读性能。
缓存是介于数据访问者和数据源之间的高速存储,当数据需要多次读取的时候,加快读取的速度。
缓存与缓冲的区别:
缓冲是介于高速与低速设备之间的临时存储,缓冲不涉及多次读取。
缓存无处不在。
内存访问速度时 SSD 的 1000 倍,SSD 是磁盘访问速度 100 倍。
缓存的数据存储结构:哈希表 O(1)
数据 key->hashcode->哈希表索引
1.1 关键指标
缓存命中率
影响缓存命中率的主要指标:
缓存键集合大小
缓存键空间是应用能够生成的所有键的数量。要想办法减少可能的缓存键数量。键数量越少,缓存的效率越高。
缓存可使用内存空间
缓存可使用内存空间直接决定了缓存对象的大小和数量。物理上能缓存的对象越多,缓存命中率就越高。
缓存对象生存时间
缓存对象生存时间称为 TTL。对象缓存的时间越长,缓存对象被重用的可能性就越高。
分类
1 通读缓存
客户端连接的是通读缓存而不是原始服务器。通读缓存给客户端返回缓存资源,并在请求未命中缓存时获取实际数据。
通读缓存包括:
代理缓存、反向代理缓存、CDN
2 旁路缓存
旁路缓存通常是一个独立存储。客户端先询问缓存需要的对象是否存在,如果存在,直接获取使用,如果不存在或已过期,客户端会连接主数据源获取对象,并将其保存回缓存以便将来使用。
旁路缓存包括:
浏览器缓存、本地缓存、远程分布式缓存
1.2 一致性哈希
作业
1.3 缓存的优势
缓存部署的越接近客户端,对系统的资源节省效果越明显。
缓存如何提高性能:
1.内存访问速度高于磁盘。
2.缓存保存最终结果,不需要中间计算和 CPU 消耗。
3.缓存降低数据库、磁盘、网络压力,使这些 I 设备有好的响应特性。
缓存是系统性能提升的大杀器:
技术简单、性能提升显著、应用场景多
1.4 合理使用缓存
1 频繁修改的数据
频繁修改导致应用还来不及读取就已失效。数据的读写比在 2:1 以上,缓存才有意义。
2 非热点数据
内存有限,不能将所有数据都缓存。如果数据没有热点,大部分还没有被再次访问就已经被挤出缓存了(LRU),那么缓存就没有意义。缓存热点数据应遵循二八定律。
3 脏读
一般会对缓存数据设置失效时间。应用要容忍一定时间的数据不一致。数据更新时,立即更新缓存会带来更多系统开销和事务一致性的问题。更新时简单通知缓存失效,是一种更稳妥的做法。
4 缓存雪崩
随着业务的发展,缓存会承担大部分的数据访问压力,当缓存服务崩溃时,数据库会因为完全不能承受剧大的访问压力而宕机,进而导致整个系统不可用。
5 缓存预热
缓存中存放的热点数据,是缓存系统利用 LRU 算法不断筛选淘汰出来的,过程较长,在这段时间,系统的性能和数据库负载都不好,那么最好在缓存系统启动的时候就把热点数据加载好。一些元数据,可以启动时加载数据库中全部数据到缓存进行预热。
6 缓存穿透
不恰当的业务、或者恶意攻击持续高并发的请求某个不存在的数据,导致所有的请求都会落到数据库上,对数据库造成很大的压力。简单的对策是将不存在的数据也缓存起来(其 value 值为 null),并设定一个较短的失效时间。
1.5 分布式缓存产品
Redis 的优势:
支持复杂的数据结构。
支持多路复用异步 I/ 高性能。
支持主从复制高可用。
支持原生集群与 share nothing 集群模式
Redis 集群的实现:
Redis 集群预分好 16384 个桶,当需要在集群中放置一个 key-value 时,根据 CRC16(key) md 16384,决定将一个 key 放到哪个桶中。
Redis-cluster 把所有的物理节点映射到[0-16383]slot 上(类似与一致性哈希),cluster 负责维护 slot 与服务器的映射关系。
所有的 Redis 节点彼此互联。客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。
二、消息队列
优化系统写性能
2.1 同步调用 VS 异步调用
同步调用:调用线程阻塞等待调用结果返回,再通知调用端。
异步调用:调用线程将请求消息发到消息队列,就返回通知调用端。后续收到调用响应,由其他线程执行当前调用模块的回调函数。
2.2 消息队列构建异步调用架构
架构角色:生产者、消息队列、消费者
消息的生产者、消费者都是异步执行的,他们通过消息队列解耦、解阻塞。
消费模型
1 点对点模型
有多个消息的生产者和消费者,但每条消息只会被消费一次。
2 发布订阅模型
生产者发布一个消息,该消息可以通过主题被多个消费者订阅,每条消息可以被多个订阅消费者同时消费。
2.3 消息队列的好处
1 实现异步处理,提升性能。
主要是写性能。
2 更好的伸缩性。
调用方、被调用方解耦后,消费者可按需对算力、存储空间有针对性伸缩。
3 削峰填谷。
避免消费者被流量压垮,也避免按业务高峰部署系统造成资源浪费。
4 失败隔离与自我修复。
生产者不直接依赖消费者,可对消费者灵活的执行运维发布操作。
5 解耦。
隔离和简化开发流程。
2.4 事件驱动架构 EDA
基于发布订阅模型。
系统更加解耦。
个人理解 EDA 更适合低实时需求的系统。而电信系统通常使用点对点消息模型。
2.5 MQ 产品
RabbitMQ
ActiveMQ
RcketMQ
Kafka
产品选型时,尽量选社区活跃,资料多,出问题容易查到解决方案的产品。
一个简单比较的方式是:将产品关键字在搜索引擎中的搜索结果数做排序。
三、负载均衡
3.1 分类
1 HTTP 重定向负载均衡
实现方法简单。多一次网络请求,效率比较低。应用服务器对外暴露 IP,安全性差。现实中应用的不多。
2 DNS 负载均衡
域名服务商提供 DNS,配置简单。此方案在现实中大型互联网应用比较多,虽然对外暴露了 IP,但大型互联网往往有两级负载均衡,暴露的只是内部某一个负载均衡服务器的 IP。
3 反向代理负载均衡
配置简单,在小规模系统上应用较广。所有流量都通过反向代理服务器做应用层协议转换,效率较低,只能承担小规模集群负载均衡。
4 IP 负载均衡
只进行 IP 层的包转发(NAT),性能更高,可处理更大规模的集群。响应数据包通常比较大,所有响应流量都通过负载均衡服务器,给服务器网卡带宽带来压力。
5 数据链路层负载均衡
负载均衡服务器跟所有应用服务器使用相同 IP 地址,负载均衡服务器修改目的 Mac 地址的包转发,应用服务器直接将响应返回给客户端,大大降低了负载均衡服务器的反向带宽压力。
3.2 算法
轮询
加权轮询
随机
最少连接
源地址散列 实现会话粘滞
3.3 Session 管理
1 Session 复制
服务期间复制 session 比较消耗资源,很难提升性能和大规模伸缩,实际中很少使用
2 Session 绑定
基于源地址散列,由于没考虑服务器高可用问题导致 session 失效,实际中很少使用。
3 利用 Cookie 记录 Session
实现比较简单,不依赖于后台服务器。Cookie 大小有限,不适合记录比较大的 session。有些浏览器会禁用 cookie。在实践中较常用。
4 Session 服务器
应用服务器之间 share nothing,无状态伸缩,是最高效的分布式实现方式。
四、后续扩展学习
1 缓存的几种读方式:cache through, cache aside
2 缓存使用的坑:热点、预热、雪崩、穿透、脏读
3 Redis 集群配置和使用(go java 相关 api 中间件),相关配置参数,性能
4 Redis 作为数据库 cache 的最佳实践,用什么结构、如何跟 db 同步数据
5 Redis 作为 nosql,其数据结构在各种业务场景的最佳实践
6 缓存、Redis 在数据结构上的实现:LRU、Zset 跳表、一致性哈希算法
7 多线程编程模型:同步/异步 阻塞/非阻塞。结合项目。
8 了解一个 EDA 架构(微服务中的 SATA?)
9 RabbitMQ、kafka 的配置、集群和使用(go java 相关 api 中间件),相关配置参数,性能
10 反向代理服务器的内部功能,与负载均衡服务器,API Gateway 的区别。
评论