内容简介:做到集群高性能其实非常简单,三个字【加机器】但加机器之后带来的是机器之间配合和集群管理的复杂度,对应也会有解决方案任务分配是高性能集群主要的复杂性体现,所以选择一个合适的负载均衡器和算法是必须的
做到集群高性能其实非常简单,三个字【加机器】
但加机器之后带来的是机器之间配合和集群管理的复杂度,对应也会有解决方案
负载均衡
任务分配是高性能集群主要的复杂性体现,所以选择一个合适的负载均衡器和算法是必须的
分类
DNS
最简单的方式,一般用来实现地理级别的均衡(AWS Route53就是)。比如来自美国的访问就分配给位于美国的服务器,来自中国的访问就分配给位于中国的服务器
优点:
- 简单、成本低:负载均衡交给了DNS服务器做,不需要自己维护设备
- 提升访问速度:实现了就近访问,提升性能
缺点:
- 更新不及时:DNS有一定的缓存时间,一旦修改DNS配置,还是会访问到之前的服务,导致失败,这也不是负载均衡的目的
- 扩展性差:无法根据业务特点做一些特殊处理和负载均衡策略
硬件负载均衡
单独通过硬件设备来实现负载均衡,比如F5、A10
优点:
- 功能强大:支持各个层级的负载均衡、全面的负载均衡算法
- 性能强大:100万以上的并发
- 稳定性高:商用负载均衡经过大规模使用表现稳定
- 安全防护:防火墙、防DDOS功能
缺点:贵
软件负载均衡
单独通过负载均衡软件来实现负载均衡。比如Nginx(7层)、LVS(4层),区别在于协议和灵活性,Nginx支持HTTP、Email协议以及对应逻辑处理,LVS支持TCP,所有TCP协议基础的都能做
优点:
- 简单:维护、部署
- 便宜:不要钱
- 灵活:4层和7层的选择、业务扩展的选择
缺点:
- 性能一般
- 功能不如硬件负载均衡
- 没有防火墙
典型负载均衡架构
- 地理级别:分别部署服务器到广州、北京、上海三个机房,用户访问时,DNS路由到对应机房
- 集群级别:每个机房都用上F5,将请求路由到上海集群1或2或3/广州集群1或2或3/北京集群1或2或3
- 机器级别:通过Nginx路由到机器1或2
算法
轮询
不关心服务器负荷状态,直接按顺序轮流分配到服务器上,只要服务器在运行,就分配,即使服务器负载特别高难以处理请求,这样明显不合理,但优点是简单
加权轮询
负载均衡系统根据配置的权重,将请求路由到对应服务器上,比如某些机器性能高,某些机器性能差,就将高权重分配给性能高的机器
负载最低优先
将请求路由到当前负载最低的服务器上,比如Nginx可以通过HTTP请求数量来判断服务器当前负载,还可以自己写一个根据CPU压力来分配的负载均衡系统,但是系统复杂度上升,甚至需要为负载均衡器单独开发算法并部署然后碰到一堆莫名其妙的问题,实际的应用场景反而没有轮询用的多
性能最优
将请求分配给处理速度最快的服务器,从而达到最快响应速度,但是同样存在判断处理速度的算法问题
Hash
根据请求的IP或者其他信息进行Hash运算,将相同Hash值的请求分配到同一台服务器上,这主要是为了满足业务需求,比如不用分布式Session的集群就可以通过这个方式来实现Session,用户的Session只保存在某台服务器上,后台不需要同步到所有机器或持久化下来;或者AB测试中将某些用户分配到固定的机器上实现粘性会话
缓存
除了任务分配、其他中间件的使用也是能够大大提升集群性能的,特别是缓存、消息队列,在特定的场景下有非常惊人的效果
比如在多读少写、不写的情景下,存储系统的写性能没问题,但读性能远远不够。下面是一些常见的缓存架构存在的问题,避免这些问题就能做出一个好的缓存架构
缓存问题
缓存穿透
查询一个数据库中不存在的数据,比如商品详情,查询一个不存在的ID,如果此时去查数据库,那么每次都会访问数据库,如果有人恶意破坏,很可能直接对数据库造成过大地压力
当通过某一个key去查询数据的时候,如果对应在数据库中的数据都不存在,我们将此key对应的value设置为一个默认的值。
缓存雪崩
在高并发的环境下,如果此时key对应的缓存失效,此时有多个进程就会去同时去查询数据库,然后再去同时设置缓存。这个时候如果这个key是系统中的热点key或者同时失效的数量比较多时,数据库访问量会瞬间增大,造成过大的压力
将系统中key的缓存失效时间均匀地错开、或者通过分布式锁加锁保护
热点key
缓存中的某些Key(可能对应用与某个促销商品)对应的value存储在集群中一台机器,使得所有流量涌向同一机器,成为系统的瓶颈,该问题的挑战在于它无法通过增加机器容量来解决。
- 客户端热点key缓存:将热点key对应value并缓存在客户端本地,并且设置一个失效时间。
- 将热点key分散为多个子key,然后存储到缓存集群的不同机器上,这些子key对应的value都和热点key是一样的。
使用策略
延迟加载
读:当读请求到来时,先从缓存读,如果读不到就从数据库读,读完之后同步到缓存且添加过期时间
写:当写请求到来时,只写数据库
优点:仅对请求的数据进行一段时间的缓存,没有请求过的数据就不会被缓存,节省缓存空间;节点出现故障并不是致命的,因为可以从数据库中得到
缺点:缓存数据不是最新的;【缓存击穿】;【缓存雪崩】
直写
读:当读请求到来时,先从缓存读,如果读不到就从数据库读,读完之后同步到缓存且设置为永不过期
写:当写请求到来时,先写数据库然后同步到缓存,设置为永不过期
优点:缓存数据是最新的,无需担心缓存击穿、失效问题,编码方便
缺点:大量数据可能没有被读取的资源浪费;节点故障或重启会导致缓存数据的丢失直到有写操作同步到缓存;每次写入都需要写缓存导致的性能损失
永不过期的缓存会大量占用空间,可以设置过期时间来改进,但是会引进【缓存失效】问题,需要注意解决
如何选择
如果需要缓存与数据库数据保持实时一致,则需要选择直写方式
如果缓存服务很稳定、缓存的可用空间大、写缓存的性能丢失能够接受,选择直写方式比较方便实现
否则选择延迟加载,同时注意解决引进的问题
缓存架构
主从
用一个 redis 实例作为主机,其余的实例作为从机。主机和从机的数据完全一致,主机支持数据的写入和读取等各项操作,而从机则只支持与主机数据的同步和读取。因而可以将写入数据的命令发送给主机执行,而读取数据的命令发送给不同的从机执行,从而达到读写分离的目的。
问题是主从模式如果所连接的redis实例因为故障下线了,没有提供一定的手段通知客户端另外可连接的客户端地址,因而需要手动更改客户端配置重新连接。如果主节点由于故障下线了,那么从节点因为没有主节点而同步中断,因而需要人工进行故障转移工作。为了解决这两个问题,在2.8版本之后redis正式提供了sentinel(哨兵)架构。 哨兵
由Sentinel节点定期监控发现主节点是否出现了故障,当主节点出现故障时,由Redis Sentinel自动完成故障发现和转移,并通知应用方,实现高可用性。
集群
redis主从或哨兵模式的每个实例都是全量存储所有数据,浪费内存且有木桶效应。为了最大化利用内存,可以采用集群,就是分布式存储。集群将数据分片存储,每组节点存储一部分数据,从而达到分布式集群的目的。
上图是主从模式与集群模式的区别,redis集群中数据是和槽(slot)挂钩的,其总共定义了16384个槽,所有的数据根据一致哈希算法会被映射到这16384个槽中的某个槽中;另一方面,这16384个槽是按照设置被分配到不同的redis节点上。
但集群模式会直接导致访问数据方式的改变,比如客户端向A节点发送GET命令但该数据在B节点,redis会返回重定向错误给客户端让客户端再次发送请求,这也直接导致了必须在相同节点才能执行的一些高级功能(如 Lua 、事务、Pipeline)无法使用。另外还会引发数据分配的一致性hash问题可以参看 这里
如何选择
- 集群的优势在于高可用,将写操作分开到不同的节点,如果写的操作较多且数据量巨大,且不需要高级功能则可能考虑集群
- 哨兵的优势在于高可用,支持高级功能,且能在读的操作较多的场景下工作,所以在绝大多数场景中是适合的
- 主从的优势在于支持高级功能,且能在读的操作较多的场景下工作,但无法保证高可用,不建议在数据要求严格的场景下使用
消息队列
参考
https://time.geekbang.org/column/intro/81
https://github.com/xbox1994/2018-Java-Interview/blob/master/MD/%E6%95%B0%E6%8D%AE%E5%BA%93-Redis.md号外号外
最近在总结一些针对 Java 面试相关的知识点,感兴趣的朋友可以一起维护~
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 云计算底层技术之高性能集群趣谈
- Nginx+Tomcat搭建高性能负载均衡集群的实现方法
- 利用Nginx轻松搭建高性能负载均衡服务器集群
- 分布式高性能 Redis 集群线上常见问题
- 腾讯QQgame高性能服务器集群架构看分布式架构设计原则
- 使用Nginx+Tomcat+Keepalived 搭建高性能高可用性负载均衡集群
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。