week5- 总结 技术选型
一,分布式缓存架构
1, 什么是缓存
缓存:存储在计算机上的一个原始数据复制集,以便于访问。 -- 维基百科
缓存是介于数据访问者和数据源之间的一种高速存储,当数据需要多次读取的时候,用于加快读取的速度。
缓存(Cache) 和 缓冲(Buffer) 的分别
缓存:一般是为了数据多次读取。
缓冲:比如 CPU 写到 把数据先硬盘,因为硬盘比较慢,先到缓冲设备 Buffer,不如内存,Buffer 读和写都需要。
2, 无所不在的缓存
CPU 缓存
操作系统缓存
数据库缓存
JVM 编译缓存
CDN 缓存
代理与反向代理缓存
前端缓存
应用程序缓存
分布式对象缓存
3, 缓存数据存储
缓存数据存储:一般数据放到内存中。
主要是存储数据结构:Hash 表
业界缓存数量:百万级~亿级
4, 缓存的关键指标
缓存命中率
缓存是否有效依赖于能多少次重用同一个缓存响应业务请求,这个度量指标被称作缓存命中率。
如果查询一个缓存,十次查询九次能够得到正确结果,那么它的命中率是 90%.
影响缓存命中率的主要指标
缓存键集合大小
缓存中的每个对象使用缓存键进行识别,定位一个对象的唯一方式就是对缓存键执行精确匹配。例如,如果想为每个商品缓存在线商品信息,你需要使用商品 ID 作为缓存键。换句话说,缓存键空间是你的应用能够生成的所有键的数量。从统计数字上看,应用生成的唯一键越多,重用的机会越小。例如,如果想基于客户 IP 地址缓存天气数据,则可能有多大 40 亿个键(这是所有可能 IP 地址的数量)。如果要基于客户来源国家缓存天气数据,则可能仅需几百个缓存键(世界上所有国家的数量)。一定要想办法减少可能的缓存数量。键数量越少,缓存的效率越高
缓存可使用内存空间
缓存可使用内存空间直接解决了缓存对象的平均大小和缓存对象数量。因为缓存通常存储在内存中,缓存对象可用空间受到严格限制且相对昂贵。如果想缓存更多的对象,就需要先删除老的对象,再添加新的对象。清除对象会降低缓存命中率,因为缓存对象被删除后,将来的请求就无法命中了。物理上能缓存的对象越多,缓存命中率就越高。
缓存对象生存时间
缓存对象生存时间称为 TTL(Time To Live)。在某些场景中,例如,缓存天气预报数据 15 分钟没问题。在这个场景下,你可以设置缓存对象定义 TTL 为 15 分钟。在其它场景中,你可能不能冒险使用过于陈旧的数据。例如,在一个电子商务系统中,店铺管理员可能在任何时刻修改商品价格,如果这些价格需要准确地展示在整个网站中。在这个场景下,你需要在每个商品价格修改是让缓存失效。简单讲,对象缓存的时间越长,缓存对象被重用的可能性就越高。
5,代理缓存
6,反向代理缓存
7,多层反向代理缓存
8,cdn 内容分发网络
9,CDN 同时配置静态文件和动态内容
10,通读缓存(read-througn)
代理缓存,反向代理缓存,CDN 缓存都是通读缓存。
通读缓存给客户端返回缓存资源,并在请求未命中缓存时获取实际数据。
客户端连接的是通读缓存而不是生成相应的原始服务器。
11,旁路缓存(cache-aside)
12,浏览器对象缓存
13,本地对象缓存
对象直接缓存在应用程序内存中。
对象存储在共享内存,同一台机器的多个进程可以访问它们。
缓存服务器作为独立应用和应用程序部署在同一个服务器上
14,远程分布式对象缓存
15,Memcached 分布式对象缓存
16,Memcached 分布式缓存访问模型
怎么知道缓存放到集群中哪台服务器呢?
集群路由选择,由路由算法决定。
集群的伸缩性(添加,剔除服务器)
路由算法:
服务器地址记录到数组列表里面。
对 Key 转换为 Hash 值,Hash 值对服务器总数取模,得到的余数,就是存储到数组中指定的服务器。
17,分布式对象缓存的一致性 Hash 算法
集群的伸缩性面临的问题:比如以前只有 3 台缓存服务器,现在加了一台,变成 4 台缓存服务器。以前模 3,现在模 4,那么以前的绝大部分缓存都找不到了。就会导致服务直接访问数据库,就会没法及时反应,当前服务会阻塞,上层的服务也会阻塞。这会导致缓存击穿,增加数据库访问压力,导致整个应用服务瘫痪,也是常说的集群雪崩。
解决上述问题的方案:一致性 Hash 算法
模哈希的最大的值,2^32 次方,服务器放到环的节点上,顺时针找到余数距离最近的服务器。比如 100 台服务器,增加 1 台大道 101 台,那么只有 1%的数据访问不到。
18,基于虚拟节点的一致性 Hash 算法
一个实体服务器 V0, 那么可以虚拟出 5 台虚拟节点,比如 V0.0, V0.1, V0.2, V0.3, V0.4, V0.5,比较均匀地分布在环上,别的实体服务器也做相同的操作,最终就能平均缓存命中概率。
虚拟节点一致性 Hash 算法,能到到存储负载的均衡性。
19,各种介质数据访问延迟
缓存比数据库访问速度快 100 倍
20,技术栈各个层次的缓存
21,缓存为什么能显著提升性能
缓存数据通常来自内存,比磁盘上的数据有更快的访问速度。
缓存存储数据的最终结果形态,不需要中间计算,减少 CPU 资源的消耗。
缓存降低数据库、磁盘、网络的负载压力,使这些 I/O 设备获得更好的响应特性。
22,缓存是系统性能优化的大杀器
技术简单
性能提升显著
应用场景多
23,合理使用缓存
使用缓存对提高系统性能有很多好处,但是不合理的使用缓存可能非但不能提高系统的性能,还会成为系统的累赘,甚至风险。实践中,缓存滥用的情节屡见不鲜 -- 过分依赖缓存、不合适的数据访问特性等。
频繁修改的数据:这种数据如果缓存起来,由于频繁修改,应用还来不及读取就已经失效或更新,徒增系统负担。一般说来,数据的读写比在 2:1
以上,缓存才有意义。
24,LRU(Least Recently Used)最近最少使用到的 算法
没有热点的访问:缓存使用内存作为存储,内存的资源宝贵而有限,不能将所有数据都缓存起来,如果应用系统访问数据没有热点,不遵循二八定律,即大部分数据访问不是集中在小部分数据上,那么缓存就没有意义,因为大部分数据还没有被再次访问就已经被挤出缓存了。
纠错:这里的 FIFO 队列应该改为 FIFO 链表。
25,数据不一致与脏读
一般会对缓存的数据设置失效时间,一旦超过失效时间,就要从数据库重新加载。因此应用要容忍一定时间的数据不一致,如卖家已经编辑了商品属性,但是需要过一段时间才能被买家看到。在互联网应用中,这种延迟通常是可以接受的,但是具体应用仍需慎重对待。还要一种策略是数据更新时立即更新缓存,不过也会带来更多系统开销和事务一致性的问题。因此数据更新时通知缓存失效,删除该缓存数据,是一种更加稳妥的做法。
“计算机科学中只有三件事最困难:缓存失效,命名事物,技术错误。” -- Phil Karlton
26,缓存雪崩
缓存是为了提高数据读取性能的,缓存数据丢失或者缓存不可用不会影响到应用程序的 -- 它可以从数据库直接获取数据。但是随着业务的发展,缓存承担大部分的数据访问压力,数据库已经习惯了有缓存的日子,所以当缓存服务崩溃的时候,数据库会因为完全不能承受如此大的压力而宕机,进而导致整个网站不可用。这种情况,被称作缓存雪崩,发生这种故障,甚至不能简单的重启缓存服务和数据库服务器来回复网站访问。
27,缓存预热
缓存中存放的是热点数据,热点数据又是缓存系统利用 LRU (最近最久未用)算法对不断访问的数据筛选淘汰出来的,这个过程需要花费较长的时间,在这段时间,系统的性能和数据库负载都不太好,那么最好的缓存系统启动的时候就把热点数据加载好,这个缓存预加载叫做缓存预热(Warm up)。对于一些元数据,比如城市地名列表、类目信息,可以启动时加载数据库中全部数据到缓存进行预热。
28,缓存穿透
如果不恰当的业务、或者恶意攻击持续高并发的请求某个不存在的数据,因为缓存没有保存该数据,所有的请求都会落到数据库上,会对数据库造成很大的压力,甚至崩溃。一个简单的对策是将不存在的数据也缓存起来(其 Value 值为null
),并设定一个较短的失效时间。
29,Redis
Redis vs Memcached
Redis 支持复杂的数据结构
Redis 支持多路复用异步 I/O 高性能
Redis 支持主从复制高可用
Redis 原生集群与 Share Nothing 集群模式
Redis 集群
Redis 集群预分好
16384
个桶,当需要在 Redis 集群中放置一个 Key-Value 时,根据CRC16(key) mode 16384
的值,决定将一个 key 放到哪个桶中。Redis-Cluster 把所有的物理节点映射到
[0-16383]
Slot 上(不一定是平均分配),Cluster 负责维护 Slot 与服务器的映射关系。客户端与 Redis 节点直连,客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。
所有的 Redis 节点彼此互联。
二,消息队列和异步架构
1,同步调用
发邮件时序图:同步调用,每个调用都会阻塞等待。
同步调用:线程前后执行,都要一步一步同步等待结果。
多个耗时操作同步调用:
2,异步调用
有回调的异步调用:
3,消息队列构建异步调用架构
消息队列角色:
消息生产者
消息队列
消息消费者
模型:
点对点模型
发布订阅模型
4,消息队列的优点
实现异步处理,提升写操作处理性能
缓存提升的是读的性能,消息队列提升的是写的性能
更好的伸缩性
削峰填谷
失败隔离和自我修复
因为发布者不直接依赖消费者,所以消息系统可以将消费者系统错误与生产者系统组件隔离。
生产者和消费者互相不受双方失败影响。
这意味着任意时刻,我们都可以对后端服务器执行维护和发布操作。我们可以重启、添加或删除服务器,而不影响生产者可用性,这样简化了部署和服务器管理的难度。
解耦
5,事件驱动架构 EDA
6,主要 MQ 产品比较
RabbitMQ 的主要特点是性能好,社区活跃,但是 RabbitMQ 用 Erlang 开发,对不熟悉 Erlang 的同学而言不便于二次开发和维护。(社区 49 Million)
ActiveMQ 影响比较广泛,可以跨平台,使用 Java 开发,对 Java 比较友好。 (社区 27 Million)
RocketMQ 是阿里推出的一个开源产品,也是使用 Java 开发,性能比较哈,可靠性也比较高。(社区 35 Million)
Kafka, LinkedIn 出品,Scala 开发,专门针对分布式场景进行了优化,因此分布式的伸缩性比较好。(社区 63 Million)
三,负载均衡
1,负载均衡架构
负载均衡就是把请求分发到服务器上。
负载均衡服务器配置的应用服务器为内网 ip 地址。这样能保护内网服务器的安全。
2,HTTP 重定向负载均衡
用户访问一个 http 请求,DNS 解析域名为 ip 地址,ip 地址指向负载均衡服务器;
负责均衡服务器,获取服务器集群列表,修改 http 的 header 为集群中的某台的服务器地址,告诉客户端做 302 跳转;
客户端请求集群中的某台的服务器
集群中的某台的服务器处理完后,返回 response。
重定向服务 10 来行代码,就能实现。
缺点:
两次 http 请求,性能差。
暴露了应用服务器的公网 ip,安全性很差。
一般不会采用 HTTP 重定向负载均衡
3,DNS 负载均衡
性能方面没有问题,DNS 解析只在第一次请求的时候会用到,后面就记录在客户端里面。而且域名的解析是缓存起来的。
安全方面也没有问题。DNS 解析的 IP 地址是负载均衡服务器的 ip 地址。
这里用了 2 级负载均衡服务器。DNS,和 DNS 返回的 ip 地址都是负载均衡服务器。
4,反向代理负载均衡
小型应用会用,通常 10 几台应用服务器的时候会用。多了以后就不够用了。为什么呢?
因为反向代理服务器转发的是 http 请求,http 请求每次通信的时间比较慢,对反向代理服务器的压力比较大。当并发比较高的时候,反向代理服务器就会成为瓶颈。
5,IP 负载均衡
TCP/IP 的数据包比较小,一般是几 K。
客户端发起请求,请求到达负载均衡服务器,负载均衡服务器修改发起的 ip 为自己,目标地址为应用服务器。
应用服务器处理请求后返回给负载均衡服务器,负载均衡服务器修改发起的 ip 为客户端,目标地址为负载均衡服务器,返回给客户端。
缺点:
请求和响应的压力都在负载均衡服务器。负载均衡服务器,网卡的出口网络带宽会不够用。
6,数据链路层负载均衡
客户端请求到达负载均衡服务器,负载均衡服务器的网卡和应用服务器的网卡为一样,这是虚拟网卡。修改 Mac 地址,分发给应用服务器,达到负载均衡。
应用服务器处理完后,直接返回给客户端。
7,负载均衡算法
轮询:所有请求被依次分发到每个应用服务器上,适合于所有服务器硬件都相同的场景。
加权轮询:根据应用服务器硬件性能的情况,在轮询的基础上,按照配置的权重将请求分发到每个服务器,高性能的服务器分配更多的请求。
随机:请求被随机分配到每个应用服务器,在许多场合下,这种方案都很简单实用,因为好的随机数本身就很均衡。如果应用服务器硬件配置不同,也可以很容易的使用加权随机算法。
最少连接:记录每个应用服务器正在处理的连接数(请求数),将新到的请求分发到最少连接的服务器上,应该说,这是最符合负载均衡定义的算法。
源地址散列:根据请求来源的 IP 地址进行 Hash 计算,得到应用服务器,该算法可以保证同一个来源的请求总在一个服务器上处理,实现会话粘滞。
四,分布式数据库
1,主从复制
主服务器修改表,更新表数据操作会记录到 Binlog 里面,Binlog 会启动一个客户端线程,把更新的数据同步到从服务器的 Relay Log 里面。
更改字段的流程:
增加字段的时候,数据库表结构要先加字段,应用服务器更新再上线。
删除字段的时候,所有应用服务器先取出该字段,数据库表结构才能删掉该字段。
2,MySQL 一主多从复制
3,一主多从复制的优点
分摊负载
专机专用
便于热备份和冷备分
高可用
4,MySQL 主主复制
客户端线程更新数据,更新数据,追加到 binlog,主服务器启动客户端线程,主服务器 A 从
评论