第五周总结
1. 分布式缓存架构
1.1 什么是缓存Cache
缓存是介于数据访问者和数据源之间的一种高速存储,当数据需要多次读取的时候,用于加快读取的速度。
比如:缓存读取耗时大概0.5ms,数据库读取耗时50ms。
1.2 缓存Cache和缓冲Buffer的区别
缓存:缓存可以提高低速设备的访问速度,或者减少复杂耗时的计算带来的性能问题。缓存解决所有关于“慢”的问题,加速多次读取的速度。
缓冲区:是一块临时存储数据的区域,这些数据后面会被传输到其他设备上。缓冲区更像“消息队列篇”中即将提到的消息队列,用以弥补高速设备和低速设备通信时的速度差。
1.3 缓存的分类
CPU缓存
操作系统缓存
数据库缓存
JVM编译缓存
CDN缓存
代理与反向代理缓存
应用程序缓存
分布式对象缓存
在我们日常开发中,常见的缓存主要就是静态缓存、分布式缓存和热点本地缓存(极端的热点数据查询)这三种。
1.4 缓存的数据存储
hash表
1.5 缓存的关键指标
缓存命中率 缓存命中率 = 命中缓存的请求数 / 总请求数
在高并发系统中,核心缓存的命中率下降了一点,都会给系统带来很大的影响。
1.6 影响缓存命中率的主要指标
缓存键集合大小(键数越少,缓存的效率越高)
缓存可用内存空间(物理上能缓存的对象越多,缓存命中率就越高)
缓存对象生存时间(对象缓存的时间越长,缓存对象被重用的可能性越高)
1.7 缓存的读写策略
通读缓存(代理缓存、反向代理缓存、CDN缓存)
客户端直接从缓存中获取,如果没有命中,通读缓存再从服务中获取,并且缓存。
旁路缓存
客户端先请求缓存,如果没有在访问服务,并且把结果在写到缓存中去。
1.8 缓存的分布式集群
客户端方案就是在客户端配置多个缓存的节点,通过缓存写入和读取算法策略来实现分布式,从而提高缓存的可用性。如:取模hash、一致性hash算法
中间代理层方案是在应用代码和缓存节点之间增加代理层,客户端所有的写入和读取的请求都通过代理层,而代理层中会内置高可用策略,帮助提升缓存系统的高可用。如 Facebook 的Mcrouter,Twitter 的Twemproxy,豌豆荚的Codis。
服务端方案就是 Redis 2.4 版本后提出的 Redis Sentinel 方案。
取模hash:
对缓存的 Key 做哈希计算,然后对总的缓存节点个数取余。
这个算法最大的优点就是简单易理解,缺点是当增加或者减少缓存节点时,缓存总的节点个数变化造成计算出来的节点发生变化,从而造成缓存失效不可用。
一致性hash算法:
将整个 Hash 值空间组织成一个虚拟的圆环,然后将缓存节点的 IP 地址或者主机名做 Hash 取值后,放置在这个圆环上。当我们需要确定某一个 Key 需要存取到哪个节点上的时候,先对这个 Key 做同样的 Hash 取值,确定在环上的位置,然后按照顺时针方向在环上“行走”,遇到的第一个缓存节点就是要访问的节点。
在增加和删除节点时,只有少量的 Key 会“漂移”到其它节点上,而大部分的 Key 命中的节点还是会保持不变,从而可以保证命中率不会大幅下降。
存在的问题:数据分布不均衡,添加节点不能缓解所有节点的存储压力。
解决方案是添加虚拟节点:
将一个缓存节点计算多个 Hash 值分散到圆环的不同位置,这样既实现了数据的平均,而且当某一个节点故障或者退出的时候,它原先承担的 Key 将以更加平均的方式分配到其他节点上,从而避免雪崩的发生。
基于虚拟节点的一致性hash算法,解决了节点变动带来的缓存命中率下降和数据不均衡这两个问题。
1.9 使用缓存提升性能
缓存数据通常来自内存,比磁盘上的数据有更快的访问速度
缓存存储数据的最终结果形态,不需要中间计算,减少CPU资源的消耗。
缓存降低数据库、磁盘、网络的负载压力,使这些I/O设备获得更好的响应特性。
1.10 不要使用缓存的场景
频繁修改的数据不要使用缓存,数据的读写比在2:1以上,缓存才有意义。
没有热点的访问,不要使用缓存。
不能容忍数据不一致与脏读的场景,不要使用缓存。
1.11 缓存其他相关
缓存雪崩
缓存服务器崩溃,大量请求访问数据库,导致数据库宕机,导致整个系统不可用。
缓存预热
在缓存系统启动的时候就把热点数据加载好
缓存穿透
不恰当的业务、或者恶意攻击持续高并发的请求某个不存的数据,因为缓存没有保存该数据,所有的请求都会落到数据库上,会对数据库造成很大的压力,甚至崩溃。
回种空值和布隆过滤器是解决缓存穿透问题的两种最主要的解决方案
1.12 Redis vs Memcached
Redis支持复杂的数据结构
Redis支持多路复用异步I/O高性能
Redis支持主从复制高可用
Redis原生集群与share nothing结群模式
redis集群:
Redis集群预分配好16384个桶,当需要在Redis集群中放置一个key-value时,根据CRC16(key)mod 16384的值,决定将一个key放到那个桶中。
Redis-cluster把所有的服务节点映射到[0-16383]slot上(不一定平均分配),cluster负责维护slot与服务器的映射关系。
客户端与Redis节点直连,客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。
所有Redis节点彼此互联。
2. 消息队列与异步架构
2.1 同步调用 vs 异步调用
异步思想就是,当我们要执行一项比较耗时的操作时,不去等待操作结束,而是给这个操作一个命令:“当操作完成后,接下来去执行什么。”使用异步编程模型,虽然并不能加快程序本身的速度,但可以减少或者避免线程等待,只用很少的线程就可以达到超高的吞吐能力。
使用异步模式设计的程序可以显著减少线程等待,从而在高吞吐量的场景中,极大提升系统的整体性能,显著降低时延。
异步模型的问题:相比于同步实现,异步实现的复杂度要大很多,代码的可读性和可维护性都会显著的下降。
2.2 消息队列两种使用方式
点对点模型
点对点模型:也叫消息队列模型。系统 A 发送的消息只能被系统 B 接收,其他任何系统都不能读取 A 发送的消息。
发布订阅模型
发布 / 订阅模型:可能存在多个发布者向相同的主题发送消息,而订阅者也可能存在多个,它们都能接收到相同主题的消息。
2.3 消息队列的好处
实现异步处理,提升处理性能
更好的伸缩性
削峰填谷
失败隔离和自我修复
解耦
2.4 主要MQ产品对比
RabbitMQ,性能好,社区活跃,Erlang开发
ActiveMQ,影响比较广泛,可以跨平台,Java开发
RocketMQ,性能好,可靠性也比较高,Java开发
Kafka,专门针对分布式场景进行了优化,伸缩性好,Scala开发
3. 负载均衡架构
3.1 什么是负载均衡
将负载(访问的请求)“均衡”地分配到多个处理节点上。这样可以减少单个处理节点的请求量,提升整体系统的性能。
3.2 几种负载均衡方式
Http重定向负载均衡
缺点:
用户一次请求,需要发送两次http请求,性能不好
应用服务器暴露在公网上,安全性不好
DNS负载均衡
反向代理负载均衡
主流的中小型网站采用的方案,架构比较简单
缺点:
七层的负载均衡,代理需要等所有的数据包都收到后才进行转发,不适合高并发场景
IP负载均衡
通过修改tcp包中的ip地址,实现负载均衡,效率比较高
缺点:
负载均衡服务器出口的网络流量较大
数据链路层负载均衡
通过修改包的MAC地址来做负载均衡,适合大型网站
3.3 常用的负载均衡算法
轮询
加权轮询
随机
最少连接
源地址散列
3.4 应用服务器集群的session管理
session复制(服务器压力大,网络流量大,已经淘汰)
session绑定(粘滞session,做不到高可用,一般不用)
利用cookie记录session(浏览器有可能禁用cookie)
session服务器(主流的方案,比如使用redis集群存放分布式系统的session)
4. 分布式数据库(未讲完)
4.1 mysql一主多从复制
优点:
分摊负载
专机专用
便于冷备
高可用
4.2 mysql主主复制
注意点:
主主复制的两个数据库不能并发写入
复制只能增加数据的读并发处理能力,没有增加写并发能力和存储能力
更新表结构会倒置巨大的同步延迟
5. 总结
缓存主要是提升系统读性能,消息队列提升系统写性能。
消息队列的主要作用有:异步处理、解耦合和削峰填谷,它能提升系统的吞吐量,但是同时也增加了系统的复杂度。
mysql不管是一主多从还是主主,都不能提升存储能力,需要进行数据分片。
评论